├── .clang-format ├── .gitignore ├── .travis-test.sh ├── .travis.yml ├── Docker-build.sh ├── Docker-cleanup.sh ├── Dockerfile ├── LICENSE ├── Makefile ├── Makefile.click ├── Makefile.dpdk ├── Makefile.nfos ├── README.md ├── bench ├── .gitignore ├── ReadMe.md ├── bench.lua ├── bench.sh ├── clean.sh ├── clean │ ├── middlebox.sh │ └── tester.sh ├── config.sh ├── init-machines.sh ├── init-machines │ ├── install-dpdk.sh │ ├── install-moongen.sh │ ├── middlebox.sh │ └── tester.sh ├── init-network.sh ├── init-network │ ├── dpdk │ │ └── middlebox.sh │ ├── ipvs │ │ └── middlebox.sh │ ├── netfilter │ │ └── middlebox.sh │ └── tester.sh ├── run-benchmark.sh ├── run-middlebox.sh └── util │ ├── dpdk-functions.sh │ ├── functions.sh │ ├── netfilter-short-timeout.sh │ ├── netfilter-unlock.sh │ ├── plot │ ├── lat.r │ ├── llat.r │ ├── new-flow-lat.r │ └── thru.r │ └── relieve-connection-reuse.sh ├── click-bridge ├── Makefile └── nf.click ├── click-fw ├── Makefile └── nf.click ├── click-lb ├── Makefile └── nf.click ├── click-nat ├── Makefile └── nf.click ├── click-nop ├── Makefile └── nf.click ├── codegen ├── .gitignore ├── .merlin ├── data_spec.ml ├── data_spec.mli ├── gen-loop-boilerplate.sh ├── generate.sh ├── ir.ml ├── loop_boilerplate_gen.ml └── main.ml ├── doc ├── SpecificationSemantics.md └── VigorClickComponentsComparison.csv ├── grub.cfg ├── libvig ├── kernel │ ├── asm │ │ ├── boot.asm │ │ ├── crti.asm │ │ ├── crtn.asm │ │ ├── multiboot1_header.asm │ │ └── multiboot2_header.asm │ ├── nfos_halt.c │ ├── nfos_halt.h │ ├── nfos_main.c │ ├── nfos_pci.c │ ├── nfos_pci.h │ ├── nfos_portio.h │ ├── nfos_serial.c │ ├── nfos_serial.h │ ├── nfos_tsc.c │ ├── nfos_tsc.h │ ├── nfos_vga.c │ └── nfos_vga.h ├── models │ ├── builtin.h │ ├── dpdk-low-level.c │ ├── dpdk │ │ ├── rte_byteorder.h │ │ ├── rte_common.h │ │ ├── rte_eal.h │ │ ├── rte_errno.h │ │ ├── rte_ethdev.h │ │ ├── rte_ether.h │ │ ├── rte_ip.h │ │ ├── rte_lcore.h │ │ ├── rte_mbuf.h │ │ ├── rte_mbuf_ptype.h │ │ ├── rte_memory.h │ │ ├── rte_mempool.h │ │ ├── rte_string_fns.h │ │ ├── rte_tcp.h │ │ └── rte_udp.h │ ├── externals │ │ ├── README.md │ │ ├── __strtol_internal.c │ │ ├── __strtoll_internal.c │ │ ├── _abort.c │ │ ├── _assert.c │ │ ├── _exit.c │ │ ├── _externals.h │ │ ├── _malloc.c │ │ ├── _unused.c │ │ ├── dlfcn.c │ │ ├── endian.c │ │ ├── execinfo.c │ │ ├── fnmatch.c │ │ ├── io.c │ │ ├── limit.c │ │ ├── numa.c │ │ ├── pthread.c │ │ ├── sched_cpucount.c │ │ ├── setjmp.c │ │ ├── socket_stub.c │ │ ├── stdio-files.c │ │ ├── stdio-printf-chk.c │ │ ├── stdio-printf.c │ │ ├── stdio.c │ │ ├── strings.c │ │ ├── time.c │ │ ├── timerfd.c │ │ └── unistd.c │ ├── hardware.c │ ├── hardware.h │ ├── kernel │ │ └── klee │ │ │ └── klee.h │ ├── str-descr.h │ ├── tinynf-env │ │ ├── endian.c │ │ ├── memory.c │ │ ├── pci.c │ │ └── time.c │ └── verified │ │ ├── cht.c │ │ ├── double-chain-control.h │ │ ├── double-chain.c │ │ ├── double-map-control.h │ │ ├── double-map.c │ │ ├── ether.c │ │ ├── ether.h │ │ ├── expirator.c │ │ ├── lpm-dir-24-8-control.h │ │ ├── lpm-dir-24-8.c │ │ ├── map-control.h │ │ ├── map.c │ │ ├── packet-io-control.h │ │ ├── packet-io.c │ │ ├── vector-control.h │ │ ├── vector.c │ │ ├── vigor-time-control.h │ │ └── vigor-time.c ├── proof │ ├── arith.c │ ├── arith.gh │ ├── bitopsutils.c │ ├── bitopsutils.gh │ ├── boilerplate-assumptions.c │ ├── chain-buckets.gh │ ├── coherence.c │ ├── coherence.h │ ├── listexex.gh │ ├── listutils-lemmas.c │ ├── listutils-lemmas.gh │ ├── listutils.gh │ ├── lpm-dir-24-8-lemmas.c │ ├── lpm-dir-24-8-lemmas.gh │ ├── lpm-dir-24-8.gh │ ├── map.gh │ ├── mod-pow2.c │ ├── mod-pow2.gh │ ├── modulo.c │ ├── modulo.gh │ ├── multiset.gh │ ├── multisubset.gh │ ├── natlist.gh │ ├── nth-prop.gh │ ├── permutations.c │ ├── permutations.gh │ ├── prime.c │ ├── prime.gh │ ├── set.gh │ ├── stdex.gh │ ├── transpose-lemmas.c │ ├── transpose-lemmas.gh │ └── transpose.gh └── verified │ ├── boilerplate-util.h │ ├── cht.c │ ├── cht.h │ ├── double-chain-impl.c │ ├── double-chain-impl.h │ ├── double-chain.c │ ├── double-chain.h │ ├── double-map.c │ ├── double-map.h │ ├── emap.h │ ├── ether.c │ ├── ether.h │ ├── expirator.c │ ├── expirator.h │ ├── lpm-dir-24-8.c │ ├── lpm-dir-24-8.h │ ├── map-impl-pow2.c │ ├── map-impl-pow2.h │ ├── map-impl.c │ ├── map-impl.h │ ├── map-util.h │ ├── map.c │ ├── map.h │ ├── packet-io.c │ ├── packet-io.h │ ├── tcpudp_hdr.h │ ├── vector.c │ ├── vector.h │ ├── vigor-time.c │ └── vigor-time.h ├── linker.ld ├── moonpol ├── Makefile ├── config ├── config-generator.c └── policer.lua ├── nf-log.h ├── nf-parse.h ├── nf-util.c ├── nf-util.h ├── nf.c ├── nf.h ├── pxe-boot.sh ├── setup.sh ├── setup ├── README.md ├── dpdk.config.patch ├── dpdk.ixgbe.avoid_bit_bang.patch ├── dpdk.ixgbe.hacks.patch ├── dpdk.ixgbe.no_rxen_on_fctrl_write.patch ├── dpdk.ixgbe.rdrxctl_special_writes.patch ├── dpdk.ixgbe.unknown_eimc_bit.patch ├── dpdk.ixgbe.unknown_ralrah.patch ├── dpdk.ixgbe.unknown_swfw_sync_bit.patch ├── dpdk.ixgbe.wrong_register_dpf_pmcf.patch ├── dpdk.memalloc.patch ├── klee-uclibc.config └── uclibc │ ├── fscanf.c │ └── sscanf.c ├── template ├── Makefile ├── config.c ├── config.h ├── dataspec.ml ├── dataspec.py ├── flow.h ├── fspec.ml ├── main.c └── spec.py ├── validator ├── .gitignore ├── .merlin ├── Makefile ├── canonicalize.verify.stdout.sh ├── common_fspec.ml ├── convert-to-test.sh ├── disabled-regression-tests │ ├── basic-negative │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── basic │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── better-type-guessing │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── bridge-pointer-coherence-between-map-and-vector │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── empty-map-vec-dchain-coherent-call │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── handling-arrays-properly │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── mvc-coherent-key-abscent │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ ├── support-sexp-or │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected │ └── uint32-to-16-cast-needs-bit-and │ │ ├── config │ │ ├── expected │ │ ├── trace │ │ └── verify.stdout.expected ├── fspec_api.ml ├── import.ml ├── import.mli ├── ir.ml ├── preamble.tmpl ├── render.ml ├── render.mli ├── run-test.sh ├── test_all.sh ├── trace_prefix.ml ├── translate-spec.py ├── validator.ml └── verifier.ml ├── vigbridge ├── Makefile ├── bridge_config.c ├── bridge_config.h ├── bridge_main.c ├── dataspec.ml ├── dataspec.py ├── dyn_value.h ├── fspec.ml ├── loop-with-logical-invariants.h ├── optimize.patch ├── paygo-broadcast.py ├── paygo-drop_or_forward.py ├── paygo-learn.py ├── paygo-no_loop.py ├── paygo-refresh.py ├── paygo-static_drop.py ├── paygo-table_updates.py ├── spec.py ├── stat_key.h ├── test.sh └── testbed │ ├── Vagrantfile │ ├── bridge.sh │ ├── client.sh │ ├── redeploy-nat.sh │ ├── run-nat.sh │ ├── server.sh │ ├── src │ ├── start-nat.sh │ └── test-nat.sh ├── vigfw ├── .merlin ├── Makefile ├── dataspec.ml ├── dataspec.py ├── flow.h ├── fspec.ml ├── fw_config.c ├── fw_config.h ├── fw_flowmanager.c ├── fw_flowmanager.h ├── fw_main.c ├── loop-with-defined-invariant.h ├── optimize.patch ├── paygo-block_unsolicited.py ├── paygo-flowtable_refresh.py ├── paygo-outbound_forward.py ├── paygo-outbound_open_flow.py ├── paygo-reply_forward.py └── spec.py ├── viglb ├── Makefile ├── dataspec.ml ├── dataspec.py ├── fspec.ml ├── ip_addr.h ├── lb_backend.h ├── lb_balancer.c ├── lb_balancer.h ├── lb_config.c ├── lb_config.h ├── lb_flow.h ├── lb_main.c ├── optimize.patch ├── paygo-alloc_flow_consistent.py ├── paygo-expired_backend_realloc_flow.py ├── paygo-heartbeat_add_backend.py ├── paygo-heartbeat_no_reply.py ├── paygo-heartbeat_refresh.py ├── paygo-known_flow_forward.py ├── paygo-unknown_flow_consistent.py └── spec.py ├── vignat ├── Makefile ├── dataspec.ml ├── dataspec.py ├── flow.h ├── fspec.ml ├── nat_config.c ├── nat_config.h ├── nat_flowmanager.c ├── nat_flowmanager.h ├── nat_main.c ├── optimize.patch ├── paygo-block_packet_injection.py ├── paygo-block_unsolicited.py ├── paygo-ext_no_flow_alloc.py ├── paygo-forward_reply.py ├── paygo-int_alloc_flow.py ├── paygo-int_refresh_flow.py ├── paygo-known_flow_refresh.py ├── paygo-new_flow_forward.py ├── paygo-table_updates.py ├── saved-loop-with-log-invariant.h └── spec.py ├── vignop ├── Makefile ├── dataspec.ml ├── dataspec.py ├── fspec.ml ├── nat_config.c ├── nat_config.h ├── nop_main.c └── spec.py └── vigpol ├── Makefile ├── bkp ├── dynamic_value.h.gen.c ├── dynamic_value.h.gen.h ├── ip_addr.h.gen.c └── ip_addr.h.gen.h ├── dataspec.ml ├── dataspec.py ├── dynamic_value.h ├── fspec.ml ├── ip_addr.h ├── loop-with-defined-invariant.h ├── optimize.patch ├── paygo-drop_excess.py ├── paygo-forward_compliant.py ├── paygo-incast_add.py ├── paygo-outcast_nopolicing.py ├── policer_config.c ├── policer_config.h ├── policer_main.c ├── spec.py └── test.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: LLVM 3 | AccessModifierOffset: -2 4 | ConstructorInitializerIndentWidth: 4 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortIfStatementsOnASingleLine: false 9 | AllowShortLoopsOnASingleLine: false 10 | AlwaysBreakTemplateDeclarations: false 11 | AlwaysBreakBeforeMultilineStrings: false 12 | BreakBeforeBinaryOperators: false 13 | BreakBeforeTernaryOperators: true 14 | BreakConstructorInitializersBeforeComma: false 15 | BinPackParameters: true 16 | ColumnLimit: 80 17 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 18 | DerivePointerBinding: false 19 | ExperimentalAutoDetectBinPacking: false 20 | IndentCaseLabels: true 21 | MaxEmptyLinesToKeep: 1 22 | NamespaceIndentation: None 23 | ObjCSpaceBeforeProtocolList: true 24 | PenaltyBreakBeforeFirstCallParameter: 19 25 | PenaltyBreakComment: 60 26 | PenaltyBreakString: 1000 27 | PenaltyBreakFirstLessLess: 120 28 | PenaltyExcessCharacter: 1000000 29 | PenaltyReturnTypeOnItsOwnLine: 60 30 | PointerBindsToType: false 31 | SpacesBeforeTrailingComments: 1 32 | Cpp11BracedListStyle: false 33 | Standard: Cpp03 34 | IndentWidth: 2 35 | TabWidth: 8 36 | UseTab: Never 37 | BreakBeforeBraces: Attach 38 | IndentFunctionDeclarationAfterType: false 39 | SpacesInParentheses: false 40 | SpacesInAngles: false 41 | SpaceInEmptyParentheses: false 42 | SpacesInCStyleCastParentheses: false 43 | SpaceAfterControlStatementKeyword: true 44 | SpaceBeforeAssignmentOperators: true 45 | ContinuationIndentWidth: 4 46 | 47 | IndentPPDirectives: AfterHash 48 | MacroBlockBegin: VIGOR_LOOP_BEGIN 49 | MacroBlockEnd: VIGOR_LOOP_END 50 | SortIncludes: false 51 | AlignAfterOpenBracket: Align 52 | ... 53 | 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.results 3 | *.pdf 4 | ~* 5 | *~ 6 | *.aux 7 | *.cmo 8 | *.cma 9 | *.byte 10 | #* 11 | *# 12 | .#* 13 | fullpage.sty 14 | *.vo 15 | *.glob 16 | *.cache 17 | *.bc 18 | *.os 19 | *.ll 20 | *.o 21 | *.a 22 | *.iso 23 | *.bin 24 | *.d 25 | *.cmd 26 | build/ 27 | TAGS 28 | .vagrant 29 | klee-*/ 30 | klee-last 31 | */*.gen.* 32 | */loop.* 33 | */state.* 34 | .DS_Store 35 | *.swp 36 | .vscode/* 37 | *.vfmanifest 38 | fspec_gen.ml 39 | -------------------------------------------------------------------------------- /.travis-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ../paths.sh 4 | 5 | set -euxo pipefail 6 | 7 | for nf in vig*; do 8 | pushd "$nf" 9 | make 10 | make symbex #validate is too slow for Travis :( 11 | popd 12 | done 13 | 14 | echo "All NFs succeeded" 15 | 16 | make verifast 17 | 18 | echo "All verifast checks pass" 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | language: minimal 3 | 4 | services: 5 | - docker 6 | 7 | before_install: 8 | - docker pull dslabepfl/vigor-20.08 9 | 10 | install: 11 | - docker run -dt --name=vigor dslabepfl/vigor-20.08 12 | - docker exec vigor rm -rf /home/vigor/vigor 13 | - docker cp . vigor:/home/vigor/vigor 14 | - docker exec vigor sudo chown -R vigor:vigor /home/vigor/vigor 15 | 16 | script: 17 | - docker exec vigor /bin/bash -c 'cd vigor && . .travis-test.sh' 18 | -------------------------------------------------------------------------------- /Docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Creates an image with the Vigor container if it doesn't already exists, and launches a container 3 | 4 | # Bash "strict mode" 5 | set -euxo pipefail 6 | 7 | 8 | VNDSDIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) 9 | KERNEL_VER=$(uname -r | sed 's/-generic//') 10 | IMAGE_NAME='dslabepfl/vigor' 11 | 12 | 13 | # Make sure we have the Linux headers 14 | sudo apt-get install -y "linux-headers-$KERNEL_VER" 15 | 16 | 17 | # Create the image if needed 18 | if [ -z "$(sudo docker images -q $IMAGE_NAME)" ]; then 19 | # HACK: Docker doesn't support absolute paths in COPY 20 | # so we create symlinks instead... 21 | # ...except Docker doesn't like that either, 22 | # so we use mount points... 23 | if [ ! -e "usr-src" ]; then 24 | mkdir usr-src 25 | sudo mount --bind /usr/src usr-src 26 | fi 27 | if [ ! -e "lib-modules" ]; then 28 | mkdir lib-modules 29 | sudo mount --bind /lib/modules lib-modules 30 | fi 31 | 32 | sudo docker image build "$VNDSDIR" --build-arg "kernel_ver=$KERNEL_VER" -t "$IMAGE_NAME" 33 | 34 | # ... and then delete them 35 | sudo umount usr-src 36 | rmdir usr-src 37 | sudo umount lib-modules 38 | rmdir lib-modules 39 | fi 40 | 41 | # Run the container 42 | sudo docker run -i -t "$IMAGE_NAME" 43 | -------------------------------------------------------------------------------- /Docker-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Removes the temporary files produced by variaous builds in the setup.sh 3 | # also cleans up the apt cache, as we already installed all the packages 4 | # necessary 5 | 6 | set -euxo pipefail 7 | 8 | # Delete sources used to build our toolchain from scratch 9 | rm -rf binutils gcc 10 | 11 | # We don't need tests and tmp files 12 | # And definitely no SVN or GIT 13 | # Most of the object files are redundant as well (but not all) 14 | find . -name .svn | xargs rm -rf 15 | find . -name .git | xargs rm -rf 16 | find llvm -name test | xargs rm -r 17 | find llvm -name unittests | xargs rm -r 18 | find . -name '*.tmp' -delete 19 | find llvm/lib/Target llvm/tools/clang -name '*.inc' -delete 20 | find klee -name '*.a' -delete 21 | find klee -name '*.o' -delete 22 | find klee-uclibc klee-uclibc-binary -name '*.o' -delete 23 | find fastclick fastclick-batch -name '*.o' -delete 24 | find z3 -name '*.o' -delete 25 | find dpdk libmoon/deps/dpdk -name app | xargs rm -rf 26 | 27 | # Clean the apt cache 28 | sudo apt-get clean autoclean 29 | sudo apt-get autoremove --yes 30 | sudo rm -rf /var/lib/apt/lists/* 31 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | # Linux version (WITHOUT the -generic) 4 | ARG kernel_ver 5 | 6 | # Get the kernel stuff (since Docker shares the kernel with the host) 7 | COPY ./usr-src/linux-headers-${kernel_ver} /usr/src/linux-headers-${kernel_ver} 8 | COPY ./usr-src/linux-headers-${kernel_ver}-generic /usr/src/linux-headers-${kernel_ver}-generic 9 | COPY ./lib-modules/${kernel_ver}-generic /lib/modules/${kernel_ver}-generic 10 | 11 | # The install script requires sudo (no need to clean apt cache, the setup script will install stuff) 12 | RUN apt-get update && apt-get install -y sudo 13 | 14 | # Create (-m == with a homedir) and use an user with passwordless sudo 15 | RUN useradd -m vigor \ 16 | && echo "vigor:vigor" | chpasswd \ 17 | && echo 'vigor ALL=(root) NOPASSWD: ALL' >> /etc/sudoers 18 | USER vigor 19 | WORKDIR /home/vigor 20 | 21 | # Copy everything from the repo 22 | COPY --chown=vigor:vigor . /home/vigor/vigor 23 | # (except for /usr and /lib mount points) 24 | RUN rm -rf /home/vigor/vigor/.git && sudo rm -rf /home/vigor/vigor/lib-modules && sudo rm -rf /home/vigor/vigor/usr-src 25 | # Execute the setup script 26 | RUN /home/vigor/vigor/setup.sh && /home/vigor/vigor/Docker-cleanup.sh 27 | # Pass -l to bash so it reads ~/.profile 28 | ENTRYPOINT ["/bin/bash", "-l"] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dependable Systems Laboratory / EPFL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.click: -------------------------------------------------------------------------------- 1 | # Makefile for Click baseline NFs 2 | 3 | CLICK_DIR := $(VIGOR_DIR)/fastclick 4 | CLICK_BATCH_SIZE := 1 5 | ifeq (true,$(VIGOR_USE_BATCH)) 6 | CLICK_DIR := $(VIGOR_DIR)/fastclick-batch 7 | CLICK_BATCH_SIZE := 32 8 | endif 9 | 10 | NF_PROCESS_NAME := click 11 | 12 | compile: 13 | @echo 'No compile needed for Click NFs!' 14 | 15 | run: 16 | @printf '\n\n!!!\nClick may incorrectly claim that the CPU\n' 17 | @printf 'cores are not on the same NUMA node as the NICs, ignore this.\n!!!\n\n' 18 | @sudo $(CLICK_DIR)/bin/click burst=$(CLICK_BATCH_SIZE) --dpdk $(NF_DPDK_ARGS) -- nf.click 19 | -------------------------------------------------------------------------------- /bench/.gitignore: -------------------------------------------------------------------------------- 1 | # Used by benchmark scripts to avoid checking for dependencies every time 2 | .deps_version 3 | -------------------------------------------------------------------------------- /bench/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Hardware testbench 2 | 3 | This folder contains the scripts necessary to benchmark Vigor NFs, Linux's NAT (NetFilter), 4 | and any other DPDK-based software obeying VigNAT's public parameters "API", such as the provided nop and unverified nat applications. 5 | 6 | ## Hardware setup 7 | 8 | 2 machines are required, each with an interface connected to the outside world and an associated hostname. 9 | All machines must be able to talk to each other by going through the outside world. 10 | As Vigor NFs are DPDK-based apps, the machines must have DPDK-compatible NICs. 11 | 12 | The 1st machine is the tester, the 2nd is the middlebox. 13 | There must be 2 connections between tester and middlebox. 14 | 15 | ## Software setup 16 | 17 | Create an SSH key on the NF and add its public key to the tester, as the scripts use SSH extensively. 18 | 19 | Edit the `config.sh` file to set the machines' names and addresses. 20 | 21 | ## Running benchmarks 22 | 23 | Benchmarks are run from the NAT machine. 24 | 25 | The single entry point to all benchmarks is `bench.sh`. It takes two arguments. 26 | The first argument is either `netfilter` or the path to a VigNAT-like app. 27 | The second argument is the scenario, one of: 28 | - `throughput` to measure maximal throughput with <0.1% loss; 29 | - `latency` to measure latency under load; 30 | 31 | The script outputs a `.results` file with the results. When testing a VigNAT-like app, a `.log` file will also be generated containing the standard output of the app. 32 | -------------------------------------------------------------------------------- /bench/bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # Master script to benchmark VigNAT-related programs. 5 | # Can benchmark different implementations, including non-NATs, 6 | # using different scenarios. 7 | 8 | # Parameters: 9 | # $1: The app, either a known name or a DPDK NAT-like app. 10 | # Known names: "netfilter", "ipvs". 11 | # Otherwise, a folder name containing a DPDK NAT-like app, e.g. "/home/solal/vnds/vignat" 12 | # $2: The scenario, see run-benchmark.sh for details 13 | 14 | MIDDLEBOX=$1 15 | SCENARIO=$2 16 | 17 | if [ -z $MIDDLEBOX ]; then 18 | echo "[bench] No app specified" 1>&2 19 | exit 1 20 | fi 21 | 22 | if [ -z $SCENARIO ]; then 23 | echo "[bench] No scenario specified" 1>&2 24 | exit 2 25 | fi 26 | 27 | 28 | RESULTS_FILE="benchmark-$SCENARIO.results" 29 | if [ -f "$RESULTS_FILE" ]; then 30 | rm "$RESULTS_FILE" 31 | fi 32 | 33 | 34 | ./init-machines.sh 35 | ./clean.sh $MIDDLEBOX 36 | ./init-network.sh $MIDDLEBOX 37 | ./run-middlebox.sh $MIDDLEBOX $SCENARIO 38 | ./run-benchmark.sh $MIDDLEBOX $SCENARIO $RESULTS_FILE 39 | ./clean.sh $MIDDLEBOX 40 | -------------------------------------------------------------------------------- /bench/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # $1: The middlebox path (so we know what to kill) 5 | 6 | echo "[clean] Cleaning machines..." 7 | ssh $TESTER_HOST "~/scripts/clean/tester.sh" 8 | ./clean/middlebox.sh $1 9 | -------------------------------------------------------------------------------- /bench/clean/middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # $1: The middlebox path (so we know what to kill) 5 | MIDDLEBOX=$1 6 | 7 | if [ -z "$MIDDLEBOX" ]; then 8 | echo 'No middlebox given to clean' 9 | exit 1 10 | fi 11 | 12 | echo "[clean] Killing middlebox..." 13 | pushd $MIDDLEBOX >> /dev/null 14 | sudo killall -SIGKILL $(make -s _print-processname) > /dev/null 2>&1 15 | popd >> /dev/null 16 | -------------------------------------------------------------------------------- /bench/clean/tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ~/scripts/config.sh 3 | 4 | echo "[clean] Killing moongen on tester..." 5 | sudo pkill -9 MoonGen 6 | -------------------------------------------------------------------------------- /bench/config.sh: -------------------------------------------------------------------------------- 1 | # --------- # 2 | # Middlebox # 3 | # --------- # 4 | 5 | export MB_CPU=6 # the index of the CPU on which the middlebox will run 6 | export MB_HOST=icnalsp3s3.epfl.ch 7 | export MB_PCI_INTERNAL=0000:06:00.1 8 | export MB_PCI_EXTERNAL=0000:06:00.0 9 | 10 | 11 | # ------ # 12 | # Tester # 13 | # ------ # 14 | 15 | export TESTER_HOST=icnalsp3s4.epfl.ch 16 | export TESTER_PCI_INTERNAL=0000:06:00.1 17 | export TESTER_PCI_EXTERNAL=0000:06:00.0 18 | 19 | 20 | # ----- # 21 | # Other # 22 | # ----- # 23 | 24 | # Do not change unless Linux or DPDK change! 25 | export KERN_NIC_DRIVER=ixgbe 26 | export DPDK_NIC_DRIVER=igb_uio 27 | 28 | # Change only if you know what you're doing (e.g. you installed DPDK to a different path than home) 29 | if [ "$RTE_SDK" = '' ]; then 30 | export RTE_SDK=$HOME/dpdk 31 | export RTE_TARGET=x86_64-native-linuxapp-gcc 32 | fi 33 | 34 | # --- # 35 | # Old # 36 | # --- # 37 | 38 | # No need to touch this unless you want to resurrect our old benchmarks for linux-based middleboxes 39 | export MB_DEVICE_INTERNAL=p802p2 40 | export MB_DEVICE_EXTERNAL=p802p1 41 | export MB_IP_INTERNAL=192.168.6.2 42 | export MB_IP_EXTERNAL=192.168.4.2 43 | export MB_IPS_BACKENDS="192.168.4.3 192.168.4.4 192.168.4.5 192.168.4.6" 44 | export TESTER_MAC_INTERNAL=90:e2:ba:55:12:21 45 | export TESTER_MAC_EXTERNAL=90:e2:ba:55:12:20 46 | export TESTER_IP_INTERNAL=192.168.6.5 47 | export TESTER_IP_EXTERNAL=192.168.4.10 48 | -------------------------------------------------------------------------------- /bench/init-machines.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | echo "[init] Cloning scripts..." 5 | rsync -a -q --exclude '*.log' --exclude '*.results' ./ "$TESTER_HOST:scripts" 6 | 7 | echo "[init] Initializing all machines..." 8 | ssh "$TESTER_HOST" '~/scripts/init-machines/tester.sh' 9 | ./init-machines/middlebox.sh 10 | -------------------------------------------------------------------------------- /bench/init-machines/install-dpdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # See http://dpdk.org/doc/quick-start 3 | 4 | # DPDK release to install 5 | DPDK_RELEASE='17.11' 6 | 7 | if [ "$RTE_SDK" = '' ]; then 8 | export RTE_SDK="$HOME/dpdk" 9 | fi 10 | 11 | # Check if it's already installed; we manually create a file with the version 12 | if [ ! -f "$RTE_SDK/.version" ] || [ "$(cat "$RTE_SDK/.version")" != "$DPDK_RELEASE" ]; then 13 | echo "[init] DPDK not found or obsolete, installing..." 14 | 15 | # Install required packages 16 | sudo apt-get install -yqq wget build-essential linux-headers-`uname -r` 17 | 18 | # If the directory already exists, assume it's an older version, delete it 19 | if [ -d "$RTE_SDK" ]; then 20 | rm -rf "$RTE_SDK" 21 | fi 22 | 23 | # Download DPDK 24 | wget -O dpdk.tar.xz "https://fast.dpdk.org/rel/dpdk-$DPDK_RELEASE.tar.xz" 25 | tar xf dpdk.tar.xz 26 | mv "dpdk-$DPDK_RELEASE" "$RTE_SDK" 27 | rm dpdk.tar.xz 28 | 29 | # Compile it 30 | pushd "$RTE_SDK" >> /dev/null 31 | make install -j$(nproc) T=x86_64-native-linuxapp-gcc DESTDIR=. 32 | 33 | # Write out the version for next run 34 | echo "$DPDK_RELEASE" > .version 35 | popd >> /dev/null 36 | fi 37 | 38 | -------------------------------------------------------------------------------- /bench/init-machines/install-moongen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd $HOME >> /dev/null 4 | if [ ! -f moon-gen/.built ]; then 5 | git clone --depth=1 git://github.com/emmericp/MoonGen.git moon-gen 6 | 7 | cd moon-gen 8 | ./build.sh 9 | printf '\n\n!!!\nErrors from driver binding failures are normal.\n!!!\n\n' 10 | touch .built 11 | fi 12 | popd >> /dev/null 13 | -------------------------------------------------------------------------------- /bench/init-machines/middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "[init] Initializing middlebox..." 4 | 5 | ./init-machines/install-dpdk.sh 6 | 7 | # CHANGE THIS IF YOU CHANGE THE DEPS BELOW (this allows us to not apt-get update every time) 8 | DEPS_VERSION='1' 9 | if [ ! -f '.deps_version' ] || [ "$(cat .deps_version)" != "$DEPS_VERSION" ]; then 10 | sudo apt-get -qq update 11 | sudo apt-get install -yqq \ 12 | tcpdump git libpcap-dev \ 13 | linux-headers-3.13.0-93 \ 14 | libglib2.0-dev daemon iperf3 netperf tmux 15 | 16 | echo "$DEPS_VERSION" > '.deps_version' 17 | fi 18 | -------------------------------------------------------------------------------- /bench/init-machines/tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "[init] Initializing tester..." 4 | 5 | 6 | # CHANGE THIS IF YOU CHANGE THE DEPS BELOW (this allows us to not apt-get update every time) 7 | DEPS_VERSION='1' 8 | if [ ! -f '.deps_version' ] || [ "$(cat .deps_version)" != "$DEPS_VERSION" ]; then 9 | sudo apt-get -qq update 10 | 11 | sudo apt-get install -yqq \ 12 | tcpdump hping3 python-scapy git \ 13 | libpcap-dev libglib2.0-dev \ 14 | daemon iperf3 netperf liblua5.2-dev \ 15 | make binutils gcc \ 16 | bc cmake 17 | 18 | echo "$DEPS_VERSION" > '.deps_version' 19 | fi 20 | 21 | ~/scripts/init-machines/install-dpdk.sh 22 | ~/scripts/init-machines/install-moongen.sh 23 | -------------------------------------------------------------------------------- /bench/init-network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # Initializes the network for the specified scenario and app, 5 | # by running the appropriate scripts on all three machines. 6 | 7 | 8 | # Parameters: 9 | # $1: The app, either a known name or a DPDK NAT-like app. 10 | # Known names: "netfilter". 11 | # Otherwise, a folder name containing a DPDK NAT-like app 12 | MIDDLEBOX=$1 13 | 14 | if [ -z $MIDDLEBOX ]; then 15 | echo "[bench] No app specified" 1>&2 16 | exit 1 17 | fi 18 | 19 | NETWORK_APP="dpdk" 20 | if [ $MIDDLEBOX = "netfilter" ]; then 21 | NETWORK_APP="netfilter" 22 | elif [ $MIDDLEBOX = "ipvs" ]; then 23 | NETWORK_APP="ipvs" 24 | elif [ ! -d $MIDDLEBOX ]; then 25 | echo "Unknown middlebox app: $MIDDLEBOX" 1>&2 26 | exit 10 27 | fi 28 | 29 | if [ ! -d ./init-network/$NETWORK_APP ]; then 30 | echo "[init] Error, unknown app specified in init-network" 1>&2 31 | exit 4 32 | fi 33 | 34 | ssh "$TESTER_HOST" "~/scripts/init-network/tester.sh" 35 | 36 | ./init-network/$NETWORK_APP/middlebox.sh 37 | -------------------------------------------------------------------------------- /bench/init-network/dpdk/middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | echo "[init] Binding middlebox interfaces to DPDK..." 5 | LOADED_DPDK=0 6 | for pci in "$MB_PCI_INTERNAL" "$MB_PCI_EXTERNAL"; do 7 | if ! sudo "$RTE_SDK/usertools/dpdk-devbind.py" --status | grep -F "$pci" | grep -q "drv=$DPDK_NIC_DRIVER"; then 8 | if [ $LOADED_DPDK -eq 0 ]; then 9 | echo "[init] Initializing DPDK on middlebox..." 10 | . ./util/dpdk-functions.sh 11 | set_numa_pages 12 | load_igb_uio_module 13 | LOADED_DPDK=1 14 | fi 15 | 16 | sudo "$RTE_SDK/usertools/dpdk-devbind.py" --force --bind "$DPDK_NIC_DRIVER" "$pci" 17 | fi 18 | done 19 | -------------------------------------------------------------------------------- /bench/init-network/ipvs/middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | . ./util/functions.sh 4 | 5 | echo "[init] Binding middlebox interfaces to Linux..." 6 | for pci in "$MB_PCI_INTERNAL" "$MB_PCI_EXTERNAL"; do 7 | if ! sudo "$RTE_SDK/usertools/dpdk-devbind.py" --status | grep -F "$pci" | grep -q "drv=$KERN_NIC_DRIVER"; then 8 | sudo "$RTE_SDK/usertools/dpdk-devbind.py" --force --bind "$KERN_NIC_DRIVER" "$pci" 9 | fi 10 | done 11 | 12 | echo "[init] Configuring middlebox IPs..." 13 | sudo ifconfig $MB_DEVICE_EXTERNAL up 14 | sudo ip addr flush dev $MB_DEVICE_EXTERNAL 15 | sudo ip addr add $MB_IP_EXTERNAL/24 dev $MB_DEVICE_EXTERNAL 16 | sudo ifconfig $MB_DEVICE_INTERNAL up 17 | sudo ip addr flush dev $MB_DEVICE_INTERNAL 18 | sudo ip addr add $MB_IP_INTERNAL/24 dev $MB_DEVICE_INTERNAL 19 | 20 | echo "[init] Configuring middlebox forwarding rules..." 21 | sudo sysctl -w net.ipv4.ip_forward=1 22 | sudo_append /etc/sysctl.conf "net.ipv4.ip_forward=1" 23 | 24 | sudo ipvsadm -A -u $MB_IP_INTERNAL:5001 -s lc 25 | for BACKEND_IP in $MB_IPS_BACKENDS; do 26 | sudo ipvsadm -a -u $MB_IP_INTERNAL:5001 -r $BACKEND_IP:5001 -m 27 | done 28 | 29 | sudo arp -s $TESTER_IP_EXTERNAL $TESTER_MAC_EXTERNAL 30 | sudo arp -s $TESTER_IP_INTERNAL $TESTER_MAC_INTERNAL 31 | 32 | echo "[init] Unlocking software restrictions on middlebox NetFilter..." 33 | . ./util/netfilter-unlock.sh $MB_DEVICE_EXTERNAL 34 | -------------------------------------------------------------------------------- /bench/init-network/netfilter/middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | . ./util/functions.sh 4 | 5 | echo "[init] Binding middlebox interfaces to Linux..." 6 | for pci in "$MB_PCI_INTERNAL" "$MB_PCI_EXTERNAL"; do 7 | if ! sudo "$RTE_SDK/usertools/dpdk-devbind.py" --status | grep -F "$pci" | grep -q "drv=$KERN_NIC_DRIVER"; then 8 | sudo "$RTE_SDK/usertools/dpdk-devbind.py" --force --bind "$KERN_NIC_DRIVER" "$pci" 9 | fi 10 | done 11 | 12 | echo "[init] Configuring middlebox IPs..." 13 | sudo ifconfig $MB_DEVICE_EXTERNAL up 14 | sudo ip addr flush dev $MB_DEVICE_EXTERNAL 15 | sudo ip addr add $MB_IP_EXTERNAL/24 dev $MB_DEVICE_EXTERNAL 16 | sudo ifconfig $MB_DEVICE_INTERNAL up 17 | sudo ip addr flush dev $MB_DEVICE_INTERNAL 18 | sudo ip addr add $MB_IP_INTERNAL/24 dev $MB_DEVICE_INTERNAL 19 | 20 | echo "[init] Configuring middlebox forwarding rules..." 21 | sudo sysctl -w net.ipv4.ip_forward=1 22 | sudo_append /etc/sysctl.conf "net.ipv4.ip_forward=1" 23 | 24 | sudo iptables -F FORWARD 25 | sudo iptables -t nat -F POSTROUTING 26 | sudo iptables -t nat -A POSTROUTING -o $MB_DEVICE_EXTERNAL -j MASQUERADE 27 | sudo iptables -A FORWARD -i $MB_DEVICE_INTERNAL -o $MB_DEVICE_EXTERNAL -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT 28 | sudo iptables -A FORWARD -i $MB_DEVICE_INTERNAL -o $MB_DEVICE_EXTERNAL -j ACCEPT 29 | 30 | sudo arp -s $TESTER_IP_EXTERNAL $TESTER_MAC_EXTERNAL 31 | sudo arp -s $TESTER_IP_INTERNAL $TESTER_MAC_INTERNAL 32 | 33 | echo "[init] Unlocking software restrictions on middlebox NetFilter..." 34 | . ./util/netfilter-unlock.sh $MB_DEVICE_EXTERNAL 35 | -------------------------------------------------------------------------------- /bench/init-network/tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ~/scripts/config.sh 3 | 4 | echo "[init] Binding tester interfaces to DPDK..." 5 | LOADED_DPDK=0 6 | for pci in "$TESTER_PCI_INTERNAL" "$TESTER_PCI_EXTERNAL"; do 7 | if ! sudo "$RTE_SDK/usertools/dpdk-devbind.py" --status | grep -F "$pci" | grep -q "drv=$DPDK_NIC_DRIVER"; then 8 | if [ $LOADED_DPDK -eq 0 ]; then 9 | echo "[init] Initializing DPDK on tester..." 10 | . ~/scripts/util/dpdk-functions.sh 11 | set_numa_pages 12 | load_igb_uio_module 13 | LOADED_DPDK=1 14 | fi 15 | 16 | sudo "$RTE_SDK/usertools/dpdk-devbind.py" --force --bind "$DPDK_NIC_DRIVER" "$pci" 17 | fi 18 | done 19 | -------------------------------------------------------------------------------- /bench/run-benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # Master script to run the prepared benchmarks for Vigor-related programs. 5 | 6 | # Parameters: 7 | # $1: Path to the NF 8 | MIDDLEBOX=$1 9 | # $2: The scenario, one of the following: 10 | # "throughput": Measure throughput, find the rate at which the middlebox 11 | # starts losing 0.1% of packets. 12 | # "latency": Measure the forwarding latency for existing flows. 13 | SCENARIO=$2 14 | # $3: Results file 15 | RESULTS_FILE=$3 16 | 17 | if [ -z $MIDDLEBOX ]; then 18 | echo "[run] No scenario specified" 1>&2 19 | exit 1 20 | fi 21 | 22 | if [ -z $RESULTS_FILE ]; then 23 | echo "[bench] No results file specified" 1>&2 24 | exit 3 25 | fi 26 | 27 | if [ -f "$RESULTS_FILE" ]; then 28 | echo "[run] results file exists! exiting" 1>&2 29 | exit 4 30 | fi 31 | 32 | 33 | EXTRA='' 34 | pushd $MIDDLEBOX >> /dev/null 35 | # Without -s, make prints 'Entering directory..' messages, idk why 36 | LAYER="$(make -s _print-layer)" 37 | if [ "$(make -s _print-needsreverse)" = "true" ]; then EXTRA='-x 32'; fi 38 | popd >> /dev/null 39 | 40 | case $SCENARIO in 41 | "latency") DURATION=60;; # few probe flows per second so we need longer benchmarks 42 | "throughput") DURATION=60;; 43 | *) echo "Unknown scenario $SCENARIO" 1>&2; exit 5;; 44 | esac 45 | 46 | echo "[bench] Benchmarking..." 47 | ssh $TESTER_HOST "sudo ~/moon-gen/build/MoonGen ~/scripts/bench.lua -p 60 -d $DURATION $EXTRA $SCENARIO $LAYER 0 1" 48 | scp $TESTER_HOST:results.tsv "./$RESULTS_FILE" 49 | -------------------------------------------------------------------------------- /bench/run-middlebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./config.sh 3 | 4 | # Parameters: 5 | # $1: The app, either a known name or a folder name containing a DPDK NAT-like app 6 | MIDDLEBOX=$1 7 | # $2: The scenario, see run-middlebox.sh for details 8 | SCENARIO=$2 9 | 10 | if [ -z $MIDDLEBOX ]; then 11 | echo "[bench] No middlebox specified" 1>&2 12 | exit 1 13 | fi 14 | 15 | case $SCENARIO in 16 | "latency") EXPIRATION_TIME=1;; # we want to measure new flows' latency; see bench.lua for details 17 | "throughput") EXPIRATION_TIME=60;; 18 | *) echo "Unknown scenario $SCENARIO" 1>&2; exit 3;; 19 | esac 20 | 21 | 22 | LOG_FILE="benchmark-$SCENARIO.log" 23 | if [ -f "$LOG_FILE" ]; then 24 | rm "$LOG_FILE" 25 | fi 26 | 27 | 28 | if [ "$MIDDLEBOX" = "netfilter" -o "$MIDDLEBOX" = "ipvs" ]; then 29 | ./util/netfilter-short-timeout.sh $EXPIRATION_TIME 30 | else 31 | # convert s to us 32 | export EXPIRATION_TIME="$(echo "$EXPIRATION_TIME * 1000 * 1000" | bc)" 33 | 34 | pushd $MIDDLEBOX >> /dev/null 35 | echo "[bench] Running $MIDDLEBOX..." 36 | NF_DPDK_ARGS="-l $MB_CPU -n 2" make run > "$LOG_FILE" 2>&1 & 37 | popd >> /dev/null 38 | 39 | # Wait for it to have started 40 | sleep 20 41 | fi 42 | -------------------------------------------------------------------------------- /bench/util/functions.sh: -------------------------------------------------------------------------------- 1 | # Useful functions to perform common tasks. 2 | 3 | # Overwrites the specified file as sudo with the specified contents. 4 | # $1: The file 5 | # $2: The contents 6 | sudo_overwrite() 7 | { 8 | echo $2 | sudo tee $1 > /dev/null 9 | } 10 | 11 | # Appends the specified contents to the specified file as sudo. 12 | # $1: The file 13 | # $2: The contents 14 | sudo_append() 15 | { 16 | echo $2 | sudo tee -a $1 > /dev/null 17 | } 18 | -------------------------------------------------------------------------------- /bench/util/netfilter-short-timeout.sh: -------------------------------------------------------------------------------- 1 | 2 | . ./util/functions.sh 3 | 4 | TIMEOUT=$1 5 | 6 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_frag6_timeout $TIMEOUT 7 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_generic_timeout $TIMEOUT 8 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_icmp_timeout $TIMEOUT 9 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_icmpv6_timeout $TIMEOUT 10 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_max_retrans 0 11 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close $TIMEOUT 12 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait $TIMEOUT 13 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established $TIMEOUT 14 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait $TIMEOUT 15 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack $TIMEOUT 16 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_max_retrans $TIMEOUT 17 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv $TIMEOUT 18 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent $TIMEOUT 19 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait $TIMEOUT 20 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_unacknowledged $TIMEOUT 21 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_udp_timeout $TIMEOUT 22 | sudo_overwrite /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream2 $TIMEOUT 23 | -------------------------------------------------------------------------------- /bench/util/netfilter-unlock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ./util/functions.sh 3 | 4 | # Removes software limits to unlock the full power of NetFilter 5 | 6 | # Parameter: 7 | # $1: The interface that will be used to transmit packets 8 | 9 | 10 | if [ -z $1 ]; then 11 | echo "[util] No interface given in netfilter-unlock" 1>&2 12 | exit 1 13 | fi 14 | 15 | 16 | # 4KB send buffer, 20,480 connections max at worst case 17 | sudo_overwrite /proc/sys/net/core/wmem_max 83886080 18 | sudo_overwrite /proc/sys/net/core/wmem_default 83886080 19 | 20 | # 16KB receive buffer, 20,480 connections max at worst case 21 | sudo_overwrite /proc/sys/net/core/rmem_max 335544320 22 | sudo_overwrite /proc/sys/net/core/rmem_default 335544320 23 | 24 | # Max open files 25 | # already bigger: echo 65536 > /proc/sys/fs/filemax 26 | 27 | # Fast port recycling (TIME_WAIT) 28 | sudo_overwrite /proc/sys/net/ipv4/tcp_tw_recycle 1 29 | sudo_overwrite /proc/sys/net/ipv4/tcp_tw_reuse 1 30 | 31 | # TIME_WAIT buckets increased 32 | # already bigger: echo 65536 > /proc/sys/net/ipv4/tcp_max_tw_buckets 33 | 34 | # FIN timeout decreased 35 | sudo_overwrite /proc/sys/net/ipv4/tcp_fin_timeout 15 36 | 37 | # SYN backlog increased 38 | sudo_overwrite /proc/sys/net/ipv4/tcp_max_syn_backlog 65536 39 | 40 | # SYN cookies enabled 41 | sudo_overwrite /proc/sys/net/ipv4/tcp_syncookies 1 42 | 43 | # Local port range maximized 44 | sudo_overwrite /proc/sys/net/ipv4/ip_local_port_range "1024 65535" 45 | 46 | # Netdev backlog increased 47 | sudo_overwrite /proc/sys/net/core/netdev_max_backlog 100000 48 | 49 | # Interface transmit queuelen increased 50 | sudo ifconfig $1 txqueuelen 10000 51 | -------------------------------------------------------------------------------- /bench/util/plot/lat.r: -------------------------------------------------------------------------------- 1 | library(plyr) 2 | library(ggplot2) 3 | 4 | all_data <- data.frame(V1=integer(), V2=integer(), 5 | V3=integer(), V4=integer(), 6 | middlebox=character()) 7 | 8 | netfilter <- read.table("netfilter-latency.txt") 9 | netfilter["middlebox"] <- "NetFilter" 10 | vignat <- read.table("vignat-latency.txt") 11 | vignat["middlebox"] <- "VigNAT" 12 | counted_chains <- read.table("counted-chains-latency.txt") 13 | counted_chains ["middlebox"] <- "counted-chains" 14 | nop <- read.table("nop-latency.txt") 15 | nop["middlebox"] <- "NOP" 16 | unverified <- read.table("unverified-latency.txt") 17 | unverified["middlebox"] <- "DPDK-unverified" 18 | 19 | all_data <- rbind(all_data, netfilter) 20 | all_data <- rbind(all_data, vignat) 21 | all_data <- rbind(all_data, counted_chains) 22 | all_data <- rbind(all_data, nop) 23 | all_data <- rbind(all_data, unverified) 24 | 25 | cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") 26 | 27 | pd <- position_dodge(2) 28 | 29 | p <- ggplot(all_data, aes(x=V1/1e3, y=V3/1e3, 30 | group=middlebox, 31 | color=middlebox, 32 | shape=middlebox)) + 33 | geom_point(size=3,position=pd) + 34 | geom_line() + 35 | geom_errorbar(aes(ymin=(V3-V4)/1e3, ymax=(V3+V4)/1e3), width=.01, 36 | position=pd) + 37 | labs(title="1 way latency for ~8Kpkt/s. No churns") + 38 | xlab("# concurrent flows (K)") + 39 | ylab(bquote("1-way latency, "+mu+"s")) + 40 | theme_bw() + 41 | expand_limits(x=0,y=0) + 42 | coord_cartesian(ylim=c(0,25)) + 43 | theme( plot.margin = unit( c(0,0,0,0) , "in" ) ) 44 | 45 | ggsave(filename="latency.png") 46 | print(p) -------------------------------------------------------------------------------- /bench/util/plot/new-flow-lat.r: -------------------------------------------------------------------------------- 1 | library(plyr) 2 | library(ggplot2) 3 | library(scales) 4 | 5 | all_data <- data.frame(V1=integer(), V2=integer(), 6 | V3=integer(), V4=integer(), 7 | middlebox=character()) 8 | 9 | netfilter <- read.table("netfilter-new-flows-latency.txt") 10 | netfilter["middlebox"] <- "NetFilter" 11 | vignat <- read.table("vignat-new-flows-latency.txt") 12 | vignat["middlebox"] <- "VigNAT" 13 | vignat2 <- read.table("vignat-twice-new-flows-latency.txt") 14 | vignat2["middlebox"] <- "VigNAT (2x bigger table)" 15 | unverified <- read.table("unverified-new-flows-latency.txt") 16 | unverified["middlebox"] <- "DPDK-unverified" 17 | counted <- read.table("counted-chains-new-flows-latency.txt") 18 | counted["middlebox"] <- "Counted-chains map" 19 | 20 | all_data <- rbind(all_data, netfilter) 21 | #all_data <- rbind(all_data, vignat) 22 | #all_data <- rbind(all_data, vignat2) 23 | all_data <- rbind(all_data, unverified) 24 | all_data <- rbind(all_data, counted) 25 | 26 | cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") 27 | 28 | pd <- position_dodge(2) 29 | 30 | p <- ggplot(all_data, aes(x=V1/1e3, y=V3/1e3, 31 | group=middlebox, 32 | color=middlebox, 33 | shape=middlebox)) + 34 | geom_point(size=3,position=pd) + 35 | geom_line() + 36 | geom_errorbar(aes(ymin=(V3-V4)/1e3, ymax=(V3+V4)/1e3), width=.01, 37 | position=pd) + 38 | labs(title="1 way latency for ~8Kpkt/s. 1000 sample new flows") + 39 | xlab("# concurrent flows (K)") + 40 | ylab(bquote("1-way latency, " * mu * "s")) + 41 | theme_bw() + 42 | expand_limits(x=0,y=1) + 43 | #scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x), 44 | # labels = trans_format("log10", math_format(10^.x))) + 45 | coord_cartesian(ylim=c(0,25)) + 46 | theme( plot.margin = unit( c(0,0,0,0) , "in" ) ) 47 | 48 | ggsave(filename="new-flow-latency.png") 49 | print(p) -------------------------------------------------------------------------------- /bench/util/plot/thru.r: -------------------------------------------------------------------------------- 1 | # NOTE: This file has not been changed after some naming changes in the benchmark setup. 2 | 3 | library(plyr) 4 | library(ggplot2) 5 | 6 | all_data <- data.frame(V1=integer(), V2=integer(), 7 | V3=integer(), V4=integer(), 8 | V5=integer(), 9 | middlebox=character()) 10 | 11 | netfilter <- read.table("netfilter-mf-find-mg-1p.txt") 12 | netfilter["middlebox"] <- "NetFilter" 13 | vignat <- read.table("vignat-mf-find-mg-1p.txt") 14 | vignat["middlebox"] <- "VigNAT" 15 | counted_chains <- read.table("counted-chains-mf-find-mg-1p.txt") 16 | counted_chains ["middlebox"] <- "counted-chains" 17 | nop <- read.table("nop-mf-find-mg-1p.txt") 18 | nop["middlebox"] <- "NOP" 19 | unverified <- read.table("unverified-mf-find-mg-1p.txt") 20 | unverified["middlebox"] <- "DPDK-unverified" 21 | 22 | all_data <- rbind(all_data, netfilter) 23 | all_data <- rbind(all_data, counted_chains) 24 | all_data <- rbind(all_data, vignat) 25 | all_data <- rbind(all_data, nop) 26 | all_data <- rbind(all_data, unverified) 27 | 28 | cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") 29 | 30 | p <- ggplot(all_data, aes(x=V1/1000, y=V4/1e6, 31 | group=middlebox, 32 | color=middlebox, 33 | shape=middlebox)) + 34 | geom_line(size=1) + 35 | labs(title="Loopback throughput, 1% packets lost") + 36 | xlab("# concurrent flows (K)") + 37 | ylab(bquote("Throughput Mpkt/s")) + 38 | theme_bw() + 39 | expand_limits(x=0,y=0) 40 | 41 | ggsave(filename="thru.png") 42 | print(p) 43 | -------------------------------------------------------------------------------- /bench/util/relieve-connection-reuse.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . ~/scripts/util/functions.sh 3 | 4 | # Sacrifices some NetFilter fidelity to reuse connections faster 5 | 6 | sudo_overwrite /proc/sys/net/ipv4/tcp_fin_timeout 5 7 | sudo_overwrite /proc/sys/net/ipv4/tcp_tw_reuse 1 8 | sudo_overwrite /proc/sys/net/ipv4/tcp_tw_recycle 1 9 | -------------------------------------------------------------------------------- /click-bridge/Makefile: -------------------------------------------------------------------------------- 1 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 2 | -------------------------------------------------------------------------------- /click-bridge/nf.click: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a script based on Thomar NAT and using DPDK for I/O. One 3 | * can replace the FromDPDKDevice and ToDPDKDevice with FromDevice 4 | * and Queue -> ToDevice to use standard I/O. 5 | * 6 | * See also thomer-nat.click and mazu-nat.click 7 | * 8 | * Author: Hongyi Zhang 9 | * Modified by: Rishabh Iyer 10 | */ 11 | 12 | define( 13 | $iface0 0, 14 | $iface1 1, 15 | $queueSize 1024 16 | ); 17 | 18 | AddressInfo( 19 | port1 192.168.6.2 10.0.0.0/8 90:e2:ba:55:14:11, 20 | port2 192.168.4.10 192.168.4.10/27 90:e2:ba:55:14:10 21 | ); 22 | 23 | // Module's I/O 24 | nicIn0 :: FromDPDKDevice($iface0, BURST $burst); 25 | nicOut0 :: ToDPDKDevice ($iface0, IQUEUE $queueSize, BURST $burst); 26 | 27 | nicIn1 :: FromDPDKDevice($iface1, BURST $burst); 28 | nicOut1 :: ToDPDKDevice ($iface1, IQUEUE $queueSize, BURST $burst); 29 | 30 | br :: EtherSwitch; 31 | 32 | nicIn0 -> [0]br; 33 | br[0] -> nicOut0; 34 | nicIn1 -> [1]br; 35 | br[1] -> nicOut1; 36 | -------------------------------------------------------------------------------- /click-fw/Makefile: -------------------------------------------------------------------------------- 1 | NF_LAYER := 4 2 | 3 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 4 | -------------------------------------------------------------------------------- /click-fw/nf.click: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a script based on Thomar NAT and using DPDK for I/O. One 3 | * can replace the FromDPDKDevice and ToDPDKDevice with FromDevice 4 | * and Queue -> ToDevice to use standard I/O. 5 | * 6 | * See also thomer-nat.click and mazu-nat.click 7 | * 8 | * Author: Hongyi Zhang 9 | * Modified by: Rishabh Iyer 10 | */ 11 | 12 | define( 13 | $iface0 0, 14 | $iface1 1, 15 | $queueSize 1024 16 | ); 17 | 18 | AddressInfo( 19 | lan_interface 192.168.6.2 10.0.0.0/8 90:e2:ba:55:14:11, 20 | wan_interface 192.168.4.10 192.168.4.10/27 90:e2:ba:55:14:10 21 | ); 22 | 23 | // Module's I/O 24 | nicIn0 :: FromDPDKDevice($iface0, BURST $burst); 25 | nicOut0 :: ToDPDKDevice ($iface0, IQUEUE $queueSize, BURST $burst); 26 | 27 | nicIn1 :: FromDPDKDevice($iface1, BURST $burst); 28 | nicOut1 :: ToDPDKDevice ($iface1, IQUEUE $queueSize, BURST $burst); 29 | 30 | ee_left :: EnsureEther(0x0800, 1:1:1:1:1:0,90:e2:ba:55:14:10); 31 | ee_right :: EnsureEther(0x0800, 1:1:1:1:1:1,90:e2:ba:55:14:11); 32 | 33 | rwpattern :: IPRewriterPatterns(FW - - - -); 34 | ip_rw :: IPRewriter(pattern FW $iface0 $iface1, drop, MAPPING_CAPACITY 65536); 35 | 36 | nicIn0 -> Strip(14) -> CheckIPHeader -> [0]ip_rw; 37 | ip_rw[0] -> ee_left[0] -> nicOut1; 38 | 39 | 40 | nicIn1 -> Strip(14) -> CheckIPHeader -> [1]ip_rw; 41 | ip_rw[1] -> ee_right[0] -> nicOut0; 42 | -------------------------------------------------------------------------------- /click-lb/Makefile: -------------------------------------------------------------------------------- 1 | NF_LAYER := 3 2 | 3 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 4 | -------------------------------------------------------------------------------- /click-nat/Makefile: -------------------------------------------------------------------------------- 1 | NF_LAYER := 4 2 | 3 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 4 | -------------------------------------------------------------------------------- /click-nat/nf.click: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a script based on Thomar NAT and using DPDK for I/O. One 3 | * can replace the FromDPDKDevice and ToDPDKDevice with FromDevice 4 | * and Queue -> ToDevice to use standard I/O. 5 | * 6 | * See also thomer-nat.click and mazu-nat.click 7 | * 8 | * Author: Hongyi Zhang 9 | * Modified by: Rishabh Iyer 10 | */ 11 | 12 | define( 13 | $iface0 0, 14 | $iface1 1, 15 | $queueSize 1024 16 | ); 17 | 18 | AddressInfo( 19 | lan_interface 192.168.6.2 10.0.0.0/8 90:e2:ba:55:14:11, 20 | wan_interface 192.168.4.10 192.168.4.10/27 90:e2:ba:55:14:10 21 | ); 22 | 23 | // Module's I/O 24 | nicIn0 :: FromDPDKDevice($iface0, BURST $burst); 25 | nicOut0 :: ToDPDKDevice ($iface0, IQUEUE $queueSize, BURST $burst); 26 | 27 | nicIn1 :: FromDPDKDevice($iface1, BURST $burst); 28 | nicOut1 :: ToDPDKDevice ($iface1, IQUEUE $queueSize, BURST $burst); 29 | 30 | ee_left :: EnsureEther(0x0800, 1:1:1:1:1:0,90:e2:ba:55:14:10); 31 | ee_right :: EnsureEther(0x0800, 1:1:1:1:1:1,90:e2:ba:55:14:11); 32 | 33 | rwpattern :: IPRewriterPatterns(NAT wan_interface 0-65535 - -); 34 | ip_rw :: IPRewriter(pattern NAT $iface0 $iface1, drop, MAPPING_CAPACITY 65536); 35 | 36 | nicIn0 -> Strip(14) -> CheckIPHeader -> [0]ip_rw; 37 | ip_rw[0] -> ee_left[0] -> nicOut1; 38 | 39 | 40 | nicIn1 -> Strip(14) -> CheckIPHeader -> [1]ip_rw; 41 | ip_rw[1] -> ee_right[0] -> nicOut0; 42 | -------------------------------------------------------------------------------- /click-nop/Makefile: -------------------------------------------------------------------------------- 1 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 2 | -------------------------------------------------------------------------------- /click-nop/nf.click: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a script based on Thomar NAT and using DPDK for I/O. One 3 | * can replace the FromDPDKDevice and ToDPDKDevice with FromDevice 4 | * and Queue -> ToDevice to use standard I/O. 5 | * 6 | * See also thomer-nat.click and mazu-nat.click 7 | * 8 | * Author: Hongyi Zhang 9 | * Modified by: Rishabh Iyer 10 | */ 11 | 12 | define( 13 | $iface0 0, 14 | $iface1 1, 15 | $queueSize 1024 16 | ); 17 | 18 | // Module's I/O 19 | nicIn0 :: FromDPDKDevice($iface0, BURST $burst); 20 | nicOut0 :: ToDPDKDevice ($iface0, IQUEUE $queueSize, BURST $burst); 21 | 22 | nicIn1 :: FromDPDKDevice($iface1, BURST $burst); 23 | nicOut1 :: ToDPDKDevice ($iface1, IQUEUE $queueSize, BURST $burst); 24 | 25 | nicIn0 -> nicOut1; 26 | nicIn1 -> nicOut0; 27 | -------------------------------------------------------------------------------- /codegen/.gitignore: -------------------------------------------------------------------------------- 1 | _build/* 2 | *.gen.c 3 | *.gen.h 4 | -------------------------------------------------------------------------------- /codegen/.merlin: -------------------------------------------------------------------------------- 1 | PKG cil -------------------------------------------------------------------------------- /codegen/data_spec.ml: -------------------------------------------------------------------------------- 1 | 2 | type container = Map of string * string * string 3 | | Vector of string * string * string 4 | | CHT of string * string 5 | | DChain of string 6 | | Int 7 | | UInt 8 | | UInt32 9 | | EMap of string * string * string * string 10 | | LPM of string 11 | -------------------------------------------------------------------------------- /codegen/data_spec.mli: -------------------------------------------------------------------------------- 1 | 2 | type container = Map of string * string * string 3 | | Vector of string * string * string 4 | | CHT of string * string 5 | | DChain of string 6 | | Int 7 | | UInt 8 | | UInt32 9 | | EMap of string * string * string * string 10 | | LPM of string 11 | -------------------------------------------------------------------------------- /codegen/gen-loop-boilerplate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FILE_PATH=$1 4 | CODEGENDIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) 5 | 6 | set -euo pipefail 7 | 8 | cp $FILE_PATH $CODEGENDIR/nf_data_spec.ml 9 | 10 | pushd $CODEGENDIR > /dev/null 11 | corebuild loop_boilerplate_gen.byte > /dev/null 12 | popd > /dev/null 13 | 14 | rm $CODEGENDIR/nf_data_spec.ml 15 | 16 | $CODEGENDIR/_build/loop_boilerplate_gen.byte 17 | -------------------------------------------------------------------------------- /codegen/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CODEGENDIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd) 4 | set -euo pipefail 5 | 6 | function swap() 7 | { 8 | local TMPFILE=tmp.$$ 9 | mv "$1" $TMPFILE && mv "$2" "$1" && mv $TMPFILE $2 10 | } 11 | 12 | # enable backtraces 13 | export OCAMLRUNPARAM=b 14 | 15 | pushd $CODEGENDIR > /dev/null 16 | ocamlbuild -tag debug -pkg cil main.byte > /dev/null 17 | popd > /dev/null 18 | 19 | for FILE_PATH in $@; do 20 | PREPROC_FILE_PATH=$FILE_PATH.preproc.c 21 | gcc -E $FILE_PATH -I $CODEGENDIR/.. -I $CODEGENDIR/../libvig/models/dpdk > $PREPROC_FILE_PATH 22 | swap $FILE_PATH $PREPROC_FILE_PATH 23 | $CODEGENDIR/_build/main.byte $FILE_PATH 24 | swap $FILE_PATH $PREPROC_FILE_PATH 25 | rm $PREPROC_FILE_PATH 26 | # Check the generated file if possible 27 | if command -v verifast >/dev/null 2>&1 ; then 28 | if ! verifast -I $CODEGENDIR/.. -I $CODEGENDIR/../libvig/models/dpdk -c $FILE_PATH.gen.c > /dev/null; then 29 | echo 'Oh no! The generated code does not verify!' 30 | exit 1 31 | fi 32 | fi 33 | done 34 | -------------------------------------------------------------------------------- /codegen/ir.ml: -------------------------------------------------------------------------------- 1 | ../validator/ir.ml -------------------------------------------------------------------------------- /doc/VigorClickComponentsComparison.csv: -------------------------------------------------------------------------------- 1 | Click NAT, IPRewriter, UDPRewriter, TCPRewriter 2 | Vigor NAT, hash-map, vector, number-allocator 3 | Click Stateful Firewall, IPRewriter, UDPRewriter, TCPRewriter 4 | Vigor Stateful Firewall, hash-map, vector, number-allocator 5 | Click Multicast, IPMulticastEtherEncap, IGMP 6 | Vigor Multicast, hash-map, vector, number-allocator 7 | Click BRAS, PPPControlProtocol, GREEncap 8 | Vigor BRAS, hash-map, vector, number-allocator 9 | Click Monitoring, IPRateMonitor, TCPCollector 10 | Vigor Monitoring, hash-table, vector, number-allocator 11 | Click DPI, Classifier, IPClassifier 12 | Vigor DPI, pattern-matcher, vector 13 | Click IDS, Classifier, IPClassifier 14 | Vigor IDS, pattern-matcher, vector 15 | Click IPS, IPClassifier, IPFilter 16 | Vigor IPS, pattern-matcher, vector 17 | Click Congestion Control, RED, SetECN 18 | Vigor Congestion Control, queue, random generator 19 | Click Traffic Shaper, BandwitdthShaper, DelayShaper 20 | Vigor Traffic Shaper, queue 21 | Click Tunnel, IPEncap, IPsecESPEncap 22 | Vigor Tunnel, crypto 23 | Click Stateless Load Balancer, RatedSplitter, HashSwitch 24 | Vigor Stateless Load Balancer, -- 25 | Click Consistent Load Balancer, ConsistentHashSwitch 26 | Vigor Consistent Load Balancer, cht, hash-map, vector, number-allocator 27 | Click Bridge, Bridge 28 | Vigor Bridge, hash-map, vector, number-allocator 29 | Click Traffic Policer, Policer 30 | Vigor Traffic Policer, hash-map, vector, number-allocator 31 | Click Stateless Firewall, IPFilter(acl) 32 | Vigor Stateless Firewall, hash-map, vector 33 | Click DDoS Prevention, IPFilter(acl) 34 | Vigor DDoS Prevention, hash-map, vector 35 | Click IPv6/IPv4 Proxy, ProtocolTranslator46 36 | Vigor IPv6/IPv4 Proxy, --- 37 | -------------------------------------------------------------------------------- /grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "nfos" { 5 | multiboot2 /boot/kernel.bin 6 | boot 7 | } 8 | -------------------------------------------------------------------------------- /libvig/kernel/asm/crti.asm: -------------------------------------------------------------------------------- 1 | section .init 2 | global _init:function 3 | 4 | _init: 5 | push rbp 6 | mov rbp, rsp 7 | 8 | section .fini 9 | global _fini:function 10 | 11 | _fini: 12 | push rbp 13 | mov rbp, rsp 14 | -------------------------------------------------------------------------------- /libvig/kernel/asm/crtn.asm: -------------------------------------------------------------------------------- 1 | section .init 2 | pop rbp 3 | ret 4 | 5 | section .fini 6 | pop rbp 7 | ret 8 | -------------------------------------------------------------------------------- /libvig/kernel/asm/multiboot1_header.asm: -------------------------------------------------------------------------------- 1 | section .multiboot_header 2 | 3 | header_start: 4 | dd 0x1BADB002 ; magic number (multiboot 1) 5 | dd 3 ; multiboot flags 6 | dd -(0x1BADB002 + 3) ; header checksum 7 | 8 | header_end: 9 | -------------------------------------------------------------------------------- /libvig/kernel/asm/multiboot2_header.asm: -------------------------------------------------------------------------------- 1 | section .multiboot_header 2 | 3 | header_start: 4 | dd 0xe85250d6 ; magic number (multiboot 2) 5 | dd 0 ; architecture 0 (protected mode i386) 6 | dd header_end - header_start ; header length 7 | ; checksum 8 | dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) 9 | ; required end tag 10 | dw 0 ; type 11 | dw 0 ; flags 12 | dd 8 ; size 13 | header_end: 14 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_halt.c: -------------------------------------------------------------------------------- 1 | #ifdef KLEE_VERIFICATION 2 | # include 3 | #endif 4 | 5 | #include "nfos_halt.h" 6 | 7 | void nfos_halt(void) { 8 | 9 | #ifdef KLEE_VERIFICATION 10 | 11 | exit(1); // One does not just calls "halt" if nothing bad happened 12 | 13 | #else // KLEE_VERIFICATION 14 | 15 | while (1) { 16 | asm volatile("hlt"); 17 | } 18 | 19 | #endif // KLEE_VERIFICATION 20 | } 21 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_halt.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_HALT_H 2 | #define NFOS_HALT_H 3 | 4 | #include 5 | 6 | /* Halt the machine (spin endlessly) */ 7 | extern noreturn void nfos_halt(void) __attribute__((used)); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nfos_pci.h" 6 | #include "nfos_halt.h" 7 | #include "nfos_serial.h" 8 | 9 | extern void _init(void); 10 | extern void do_map_test(void); 11 | 12 | /* Kernel main */ 13 | extern int main(void); 14 | /* NF main */ 15 | extern int nf_main(int argc, char *argv[]); 16 | /* Initialize filesystem */ 17 | extern void stub_stdio_files_init(struct nfos_pci_nic *devs, int n); 18 | 19 | #ifdef VIGOR_MODEL_HARDWARE 20 | extern struct nfos_pci_nic *stub_hardware_get_nics(int *n); 21 | #endif 22 | 23 | int main(void) { 24 | static char *argv[] = { 25 | NF_ARGUMENTS, 26 | NULL, 27 | }; 28 | 29 | static const int argc = (sizeof(argv) / sizeof(argv[0])) - 1; 30 | 31 | #ifndef KLEE_VERIFICATION 32 | nfos_serial_init(); 33 | #endif //! KLEE_VERIFICATION 34 | 35 | int num_devs; 36 | struct nfos_pci_nic *devs; 37 | 38 | #ifdef VIGOR_MODEL_HARDWARE 39 | devs = stub_hardware_get_nics(&num_devs); 40 | #else // VIGOR_MODEL_HARDWARE 41 | devs = nfos_pci_find_nics(&num_devs); 42 | #endif // VIGOR_MODEL_HARDWARE 43 | 44 | stub_stdio_files_init(devs, num_devs); 45 | 46 | #ifndef KLEE_VERIFICATION 47 | _init(); 48 | #endif //! KLEE_VERIFICATION 49 | 50 | printf("Calling NF...\n"); 51 | nf_main(argc, argv); 52 | printf("Done\n"); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_pci.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_PCI_H 2 | #define NFOS_PCI_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define PCI_NUM_RESOURCES 6 9 | 10 | struct nfos_pci_resource { 11 | void *start; 12 | size_t size; 13 | bool is_mem; 14 | }; 15 | 16 | struct nfos_pci_nic { 17 | uint16_t vendor_id; 18 | uint16_t device_id; 19 | 20 | uint16_t subsystem_id; 21 | uint16_t subsystem_vendor_id; 22 | 23 | uint32_t class_code; 24 | 25 | struct nfos_pci_resource resources[PCI_NUM_RESOURCES]; 26 | 27 | int interrupts_fd; 28 | }; 29 | 30 | extern struct nfos_pci_nic *nfos_pci_find_nics(int *n); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_portio.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_IOPORT_H 2 | #define NFOS_IOPORT_H 3 | 4 | #include 5 | 6 | /* Used for PCI */ 7 | 8 | static inline uint8_t nfos_inb(uint16_t port) { 9 | uint8_t ret; 10 | asm volatile("inb %1, %0" : "=a"(ret) : "dN"(port)); 11 | return ret; 12 | } 13 | 14 | static inline uint16_t nfos_inw(uint16_t port) { 15 | uint16_t ret; 16 | asm volatile("inw %1, %0" : "=a"(ret) : "dN"(port)); 17 | return ret; 18 | } 19 | 20 | static inline uint32_t nfos_inl(uint16_t port) { 21 | uint32_t ret; 22 | asm volatile("inl %1, %0" : "=a"(ret) : "dN"(port)); 23 | return ret; 24 | } 25 | 26 | static inline void nfos_outb(uint8_t val, uint16_t port) { 27 | asm volatile("outb %0, %1" : : "a"(val), "dN"(port)); 28 | } 29 | 30 | static inline void nfos_outw(uint16_t val, uint16_t port) { 31 | asm volatile("outw %0, %1" : : "a"(val), "dN"(port)); 32 | } 33 | 34 | static inline void nfos_outl(uint32_t val, uint16_t port) { 35 | asm volatile("outl %0, %1" : : "a"(val), "dN"(port)); 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_serial.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_SERIAL_H 2 | #define NFOS_SERIAL_H 3 | 4 | extern void nfos_serial_init(void); 5 | extern void nfos_serial_write_char(char c); 6 | extern void nfos_serial_write_str(const char *s); 7 | extern void nfos_serial_write_int(int i); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_tsc.c: -------------------------------------------------------------------------------- 1 | #include "nfos_tsc.h" 2 | #include 3 | #include 4 | 5 | #ifdef KLEE_VERIFICATION 6 | 7 | extern uint64_t stub_rdtsc(void); 8 | 9 | uint64_t nfos_rdtsc(void) { return stub_rdtsc(); } 10 | 11 | #else // KLEE_VERIFICATION 12 | 13 | uint64_t nfos_rdtsc(void) { return __rdtsc(); } 14 | 15 | #endif // KLEE_VERIFICATION 16 | 17 | uint64_t nfos_tsc_get_freq(void) { return rte_get_tsc_hz(); } 18 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_tsc.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_TSC_H 2 | #define NFOS_TSC_H 3 | 4 | #include 5 | 6 | extern uint64_t nfos_rdtsc(void); 7 | extern uint64_t nfos_tsc_get_freq(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libvig/kernel/nfos_vga.h: -------------------------------------------------------------------------------- 1 | #ifndef NFOS_VGA_H 2 | #define NFOS_VGA_H 3 | 4 | extern void nfos_vga_write_char(char c); 5 | extern void nfos_vga_write_str(const char *s); 6 | extern void nfos_vga_write_int(int i); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /libvig/models/dpdk-low-level.c: -------------------------------------------------------------------------------- 1 | #ifdef VIGOR_MODEL_HARDWARE 2 | # include 3 | # include 4 | # include // for ixgbe_rxtx 5 | 6 | # include 7 | 8 | uint8_t rte_rtm_supported; 9 | 10 | int rte_cpu_get_flag_enabled(enum rte_cpu_flag_t feature) { 11 | // Sentinel value - see makefile 12 | if (feature == (enum rte_cpu_flag_t)424242) { 13 | return 1; // klee_int("rte_cpu_get_flag_enabled_return"); 14 | } 15 | 16 | // Nope, not supported 17 | return 0; 18 | } 19 | 20 | const char *rte_cpu_get_flag_name(enum rte_cpu_flag_t feature) { 21 | return ""; 22 | } 23 | 24 | uint64_t get_tsc_freq_arch(void) { 25 | return -1; // Not supported 26 | } 27 | 28 | uint64_t stub_rdtsc(void) { 29 | uint64_t value; 30 | klee_make_symbolic(&value, sizeof(uint64_t), "tsc"); 31 | return value; 32 | } 33 | 34 | void stub_prefetch(const volatile void *p) { 35 | // Nothing 36 | } 37 | 38 | const char *stub_strerror(int errnum) { return ""; } 39 | 40 | void stub_abort(void) { klee_silent_exit(1); } 41 | 42 | __attribute__((constructor)) static void stub_rte_init(void) { 43 | // rte_memcpy uses fancy-schmancy intrinsics 44 | klee_alias_function("rte_memcpy", "memcpy"); 45 | 46 | // rte_rdtsc uses assembly; we remain sound by modeling it as an unconstrained 47 | // symbol note that rte_rdtsc is static inline, so we alias it with a regex to 48 | // catch all instantiations 49 | klee_alias_function_regex("rte_rdtsc[0-9]*", "stub_rdtsc"); 50 | 51 | // rte_prefetch* functions use assembly, obviously 52 | klee_alias_function_regex("rte_prefetch.*", "stub_prefetch"); 53 | 54 | // Don't bother trying to translate error codes 55 | // note: this is just to avoid an snprintf, we could support it I guess... 56 | klee_alias_function("rte_strerror", "stub_strerror"); 57 | 58 | // Don't let symbex die... 59 | klee_alias_function("abort", "stub_abort"); 60 | 61 | // Use stderr for logs 62 | rte_openlog_stream(stderr); 63 | } 64 | #endif // VIGOR_MODEL_HARDWARE 65 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_byteorder.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTE_BYTEORDER_H_INCLUDED_ 2 | #define _RTE_BYTEORDER_H_INCLUDED_ 3 | 4 | #include 5 | 6 | // This file MUST mirror exactly the way dpdk does it, including making the ANDs 7 | // uint16 constants 8 | 9 | static inline uint16_t rte_cpu_to_be_16(uint16_t x) { 10 | return ((x & UINT16_C(0x00FF)) << 8) | ((x & UINT16_C(0xFF00)) >> 8); 11 | } 12 | 13 | static inline uint16_t rte_be_to_cpu_16(uint16_t x) { 14 | return ((x & UINT16_C(0x00FF)) << 8) | ((x & UINT16_C(0xFF00)) >> 8); 15 | } 16 | 17 | #endif //_RTE_BYTEORDER_H_INCLUDED_ 18 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static inline void rte_exit(int exit_code, const char *format, ...) { 6 | klee_silent_exit(exit_code); 7 | } 8 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_eal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | static inline int rte_eal_init(int argc, char **argv) { 5 | int index = 0; 6 | 7 | // Skip args until -- 8 | while (strcmp("--", argv[index])) { 9 | index++; 10 | } 11 | 12 | return index; 13 | } 14 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int rte_errno; 4 | 5 | static inline const char *rte_strerror(int errnum) { return "stub error"; } 6 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_ether.h: -------------------------------------------------------------------------------- 1 | #ifndef RTE_ETHER_H 2 | #define RTE_ETHER_H 3 | 4 | #include 5 | #include 6 | 7 | #define RTE_ETHER_TYPE_IPV4 0x0800 8 | #define RTE_ETHER_MAX_LEN 1518 9 | 10 | struct rte_ether_addr { 11 | uint8_t addr_bytes[6]; 12 | }; 13 | 14 | struct rte_ether_hdr { 15 | struct rte_ether_addr d_addr; 16 | struct rte_ether_addr s_addr; 17 | uint16_t ether_type; 18 | }; 19 | 20 | #endif // RTE_ETHER_H 21 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_ip.h: -------------------------------------------------------------------------------- 1 | // used with VeriFast, no pragma 2 | #ifndef RTE_IP_H 3 | #define RTE_IP_H 4 | 5 | #include 6 | 7 | #define RTE_IPV4_HDR_IHL_MASK 0x0F 8 | #define RTE_IPV4_IHL_MULTIXPLIER 4 9 | 10 | #define PKT_TX_TCP_SEG (1ULL << 50) 11 | 12 | struct rte_ipv4_hdr { 13 | uint8_t version_ihl; 14 | uint8_t type_of_service; 15 | uint16_t total_length; 16 | uint16_t packet_id; 17 | uint16_t fragment_offset; 18 | uint8_t time_to_live; 19 | uint8_t next_proto_id; 20 | uint16_t hdr_checksum; 21 | uint32_t src_addr; 22 | uint32_t dst_addr; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_lcore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static inline unsigned rte_socket_id(void) { return 0; } 4 | 5 | static inline unsigned rte_lcore_id(void) { return 0; } 6 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_mbuf_ptype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RTE_PTYPE_L3_IPV4 0x00000010 4 | #define RTE_ETH_IS_IPV4_HDR(ptype) ((ptype)&RTE_PTYPE_L3_IPV4) 5 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_memory.h: -------------------------------------------------------------------------------- 1 | // used with VeriFast, cannot use #pragma 2 | #ifndef RTE_MEMORY_H 3 | #define RTE_MEMORY_H 4 | 5 | #include 6 | 7 | typedef uint64_t rte_iova_t; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_mempool.h: -------------------------------------------------------------------------------- 1 | // used with VeriFast, can't use #pragma 2 | #ifndef RTE_MEMPOOL_H 3 | #define RTE_MEMPOOL_H 4 | 5 | #include 6 | 7 | #define RTE_MEMZONE_NAMESIZE 32 8 | 9 | struct rte_mempool { 10 | char name[RTE_MEMZONE_NAMESIZE]; 11 | // union { 12 | // void *pool_data; 13 | uint64_t pool_id; 14 | // }; 15 | void *pool_config; 16 | // const struct rte_memzone *mz; 17 | unsigned int flags; 18 | int socket_id; 19 | uint32_t size; 20 | uint32_t cache_size; 21 | uint32_t elt_size; 22 | uint32_t header_size; 23 | uint32_t trailer_size; 24 | unsigned private_data_size; 25 | int32_t ops_index; 26 | // struct rte_mempool_cache *local_cache; 27 | uint32_t populated_size; 28 | // struct rte_mempool_objhdr_list elt_list; 29 | uint32_t nb_mem_chunks; 30 | // struct rte_mempool_memhdr_list mem_list; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_string_fns.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // we include the actual function implementation in the Makefile 4 | // here it's just the header 5 | int rte_strsplit(char *string, int stringlen, char **tokens, int maxtokens, 6 | char delim); 7 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_tcp.h: -------------------------------------------------------------------------------- 1 | // used with VeriFast, no pragma 2 | #ifndef RTE_TCP_H 3 | #define RTE_TCP_H 4 | 5 | #include 6 | 7 | struct rte_tcp_hdr { 8 | uint16_t src_port; 9 | uint16_t dst_port; 10 | uint32_t sent_seq; 11 | uint32_t recv_ack; 12 | uint8_t data_off; 13 | uint8_t tcp_flags; 14 | uint16_t rx_win; 15 | uint16_t cksum; 16 | uint16_t tcp_urp; 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /libvig/models/dpdk/rte_udp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct rte_udp_hdr { 4 | uint16_t src_port; 5 | uint16_t dst_port; 6 | uint16_t dgram_len; 7 | uint16_t dgram_cksum; 8 | }; 9 | -------------------------------------------------------------------------------- /libvig/models/externals/README.md: -------------------------------------------------------------------------------- 1 | Models for external calls, i.e. any function that is not in our minimal KLEE-uClibc version. 2 | This includes basically anything that has side-effects. 3 | 4 | Files whose name begin with `_` are for NFOS use only. 5 | -------------------------------------------------------------------------------- /libvig/models/externals/__strtol_internal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | long __strtol_internal(const char *__nptr, char **__endptr, int __base, 5 | int __group) { 6 | // __group shall be 0 or the behavior of __strtoll_internal() is undefined 7 | assert(__group == 0); 8 | 9 | // __strtol_internal(__nptr, __endptr, __base, 0) has the same specification 10 | // as strtoll(__nptr, __endptr, __base) 11 | return strtol(__nptr, __endptr, __base); 12 | } 13 | -------------------------------------------------------------------------------- /libvig/models/externals/__strtoll_internal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | long long __strtoll_internal(const char *__nptr, char **__endptr, int __base, 5 | int __group) { 6 | // __group shall be 0 or the behavior of __strtoll_internal() is undefined 7 | assert(__group == 0); 8 | 9 | // __strtoll_internal(__nptr, __endptr, __base, 0) has the same specification 10 | // as strtoll(__nptr, __endptr, __base) 11 | return strtoll(__nptr, __endptr, __base); 12 | } 13 | -------------------------------------------------------------------------------- /libvig/models/externals/_abort.c: -------------------------------------------------------------------------------- 1 | #ifndef KLEE_VERIFICATION 2 | 3 | # include 4 | 5 | # include 6 | # include 7 | 8 | void abort(void) { 9 | printf("\n\nabort() called"); 10 | nfos_halt(); 11 | } 12 | 13 | #endif //! KLEE_VERIFICATION 14 | -------------------------------------------------------------------------------- /libvig/models/externals/_assert.c: -------------------------------------------------------------------------------- 1 | /* KLEE provides its own implementation of assert() */ 2 | #ifndef KLEE_VERIFICATION 3 | 4 | # include "nfos_halt.h" 5 | # include 6 | 7 | extern void __assert_fail(const char *msg, const char *file, int line, 8 | const char *func) { 9 | printf("\n\nAssertion failed: %s (%s: %d: %s", msg, file, line, func); 10 | 11 | nfos_halt(); 12 | } 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /libvig/models/externals/_exit.c: -------------------------------------------------------------------------------- 1 | #ifndef KLEE_VERIFICATION 2 | 3 | # include "nfos_halt.h" 4 | # include 5 | 6 | void _exit(int status) { 7 | printf("\n\n_exit(%d) called", status); 8 | nfos_halt(); 9 | } 10 | 11 | #endif //! KLEE_VERIFICATION 12 | -------------------------------------------------------------------------------- /libvig/models/externals/_externals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PAGE_SIZE 4096 6 | 7 | #define STUB_FILES_COUNT 1024 8 | #define STUB_HUGEPAGES_COUNT 8 9 | 10 | // note: we only support 1 pipe 11 | #define STUB_PIPE_FD_READ (STUB_FILES_COUNT + 1) 12 | #define STUB_PIPE_FD_WRITE (STUB_FILES_COUNT + 2) 13 | 14 | void stub_pipe_write(const void *buf, size_t len); 15 | -------------------------------------------------------------------------------- /libvig/models/externals/_malloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Doesn't look like KLEE's malloc can be bypassed so may as well not waste 3 | * the memory 4 | */ 5 | #ifndef KLEE_VERIFICATION 6 | 7 | # include 8 | # include 9 | # include 10 | # include 11 | # include 12 | # include 13 | 14 | # define MALLOC_MEM_SIZE 80000000 15 | 16 | extern void *malloc(size_t size); 17 | extern void *realloc(void *ptr, size_t new_size); 18 | extern void *calloc(size_t num, size_t size); 19 | extern void free(void *ptr); 20 | 21 | void *malloc(size_t size) { 22 | static char malloc_mem[MALLOC_MEM_SIZE] 23 | __attribute__((aligned(alignof(max_align_t)))); 24 | static size_t malloc_index; 25 | 26 | if (malloc_index + size >= MALLOC_MEM_SIZE) { 27 | abort(); 28 | } 29 | 30 | /* Ensure that memory blocks are aligned */ 31 | if ((size % alignof(max_align_t)) != 0) { 32 | size += alignof(max_align_t) - (size % alignof(max_align_t)); 33 | } 34 | 35 | void *ret = &malloc_mem[malloc_index]; 36 | malloc_index += size; 37 | 38 | return ret; 39 | } 40 | 41 | void *realloc(void *ptr, size_t new_size) { 42 | if (ptr == NULL) { 43 | return malloc(new_size); 44 | } 45 | 46 | assert(false && "Not implemented yet"); 47 | } 48 | 49 | void *calloc(size_t num, size_t size) { 50 | void *ret = malloc(num * size); 51 | memset(ret, 0, num * size); 52 | return ret; 53 | } 54 | 55 | void free(void *ptr) {} 56 | 57 | #endif /* KLEE_VERIFICATION */ 58 | -------------------------------------------------------------------------------- /libvig/models/externals/dlfcn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void *dlopen(const char *filename, int flags){ 5 | if (flags == (RTLD_LAZY | RTLD_NOLOAD) && filename != NULL) { 6 | /* "This can be used to test if the object is already resident 7 | (dlopen() returns NULL if it is not, or the object's handle 8 | if it is resident)" 9 | --https://www.man7.org/linux/man-pages/man3/dlopen.3.html 10 | Vigor is statically linked, so shared library is not already 11 | loaded, so we return NULL. 12 | */ 13 | return NULL; 14 | } 15 | klee_abort(); 16 | } 17 | -------------------------------------------------------------------------------- /libvig/models/externals/execinfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Not great but we can't emulate an arbitrarily-sized backtrace, 5 | // and anyway this is in a panic handler so not a huge deal since 6 | // it can only happen at initialization time, not while processing packets 7 | 8 | int backtrace(void **buffer, int size) 9 | { 10 | return 0; 11 | } 12 | 13 | char **backtrace_symbols(void *const *buffer, int size) 14 | { 15 | return NULL; 16 | } 17 | -------------------------------------------------------------------------------- /libvig/models/externals/fnmatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int fnmatch(const char *pattern, const char *string, int flags) { 7 | if (!strcmp(pattern, "*map_*") && !strcmp(string, ".") && flags == 0) { 8 | // Return value: 9 | // Zero if string matches pattern, FNM_NOMATCH if there is no match or 10 | // another nonzero value if there is an error. 11 | // -- http://man7.org/linux/man-pages/man3/fnmatch.3.html 12 | return FNM_NOMATCH; 13 | } 14 | 15 | klee_abort(); 16 | } 17 | -------------------------------------------------------------------------------- /libvig/models/externals/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int iopl(int level) { 4 | // Everything is in ring 0 so ok 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /libvig/models/externals/limit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef KLEE_VERIFICATION 6 | #include 7 | #endif 8 | 9 | //"The getrlimit() and setrlimit() system calls get and set resource limits." 10 | // -- https://man7.org/linux/man-pages/man2/getrlimit.2.html 11 | 12 | #ifdef KLEE_VERIFICATION 13 | static int rlim_max = 0; 14 | #endif 15 | 16 | int getrlimit(__rlimit_resource_t resource, struct rlimit *rlim) { 17 | //We need to only support only this resource for now 18 | assert(resource == RLIMIT_NOFILE); 19 | #ifdef KLEE_VERIFICATION 20 | rlim->rlim_cur = klee_int("getrlimit_rlim_cur_ret"); 21 | rlim_max = klee_int("rlim_max"); 22 | rlim->rlim_max = rlim_max; 23 | #endif 24 | return 0; 25 | } 26 | 27 | int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlim) { 28 | //We need to only support only this resource for now 29 | assert(resource == RLIMIT_NOFILE); 30 | //Do not let increase beyond maximum 31 | #ifdef KLEE_VERIFICATION 32 | klee_assert(rlim->rlim_cur <= rlim_max); 33 | #endif 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /libvig/models/externals/sched_cpucount.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // No man entry for this function. But it counts CPUs with certain masks. 6 | // In our case, we can only have 1 CPU, so let's return that. 7 | int 8 | __sched_cpucount (size_t setsize, const cpu_set_t *setp) { 9 | assert(setp != NULL); 10 | return 1; 11 | } 12 | -------------------------------------------------------------------------------- /libvig/models/externals/setjmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int sigsetjmp(sigjmp_buf env, int savesigs) { 4 | // We don't support longjmp, so nothing to do here 5 | 6 | // setjmp() and sigsetjmp() return 0 if returning directly, and nonzero when 7 | // returning from longjmp(3) or siglongjmp(3) using the saved context. 8 | // -- https://linux.die.net/man/3/sigsetjmp 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /libvig/models/externals/socket_stub.c: -------------------------------------------------------------------------------- 1 | // NOTE: The name of this file is because if it's just called "socket.c", 2 | // it doesn't get included somehow... maybe a conflict with KLEE-uclibc? 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef KLEE_VERIFICATION 9 | # include 10 | #endif 11 | 12 | int stub_socket(int family, int type, int protocol) { 13 | // "On success, a file descriptor for the new socket is returned. On error, 14 | // -1 is returned, and errno is set appropriately." 15 | // -- http://man7.org/linux/man-pages/man2/socket.2.html 16 | errno = EAFNOSUPPORT; // "The implementation does not support the specified 17 | // address family." 18 | return -1; 19 | } 20 | 21 | #ifdef KLEE_VERIFICATION 22 | 23 | __attribute__((constructor)) static void stub_socket_init(void) { 24 | klee_alias_function("socket", "stub_socket"); 25 | } 26 | 27 | #else 28 | 29 | int socket(int family, int type, int protocol) { 30 | return stub_socket(family, type, protocol); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /libvig/models/externals/stdio-printf-chk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Family of printf functions that include buffer overflow checks. 6 | // But in KLEE we don't need the checks here (KLEE does it), and in NFOS we run verified code, 7 | // so we don't need the checks. 8 | 9 | int __fprintf_chk(FILE *stream, int flag, const char *format, ...) { 10 | va_list args; 11 | va_start(args, format); 12 | int ret = vfprintf(stream, format, args); 13 | va_end(args); 14 | 15 | return ret; 16 | } 17 | 18 | int __printf_chk(int flag, const char *format, ...) { 19 | va_list args; 20 | va_start(args, format); 21 | int ret = vprintf(format, args); 22 | va_end(args); 23 | 24 | return ret; 25 | } 26 | 27 | int __snprintf_chk(char *str, size_t maxlen, int flag, size_t strlen, 28 | const char *format, ...) { 29 | /* If strlen is less than maxlen, the function shall abort and the program 30 | * shall exit */ 31 | if (strlen < maxlen) { 32 | abort(); 33 | } 34 | 35 | va_list args; 36 | va_start(args, format); 37 | int ret = vsnprintf(str, maxlen, format, args); 38 | va_end(args); 39 | 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /libvig/models/externals/strings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // DPDK 17.11 uses this even though it's nonstandard 5 | char* index(const char* s, int c) 6 | { 7 | return strchr(s, c); 8 | } 9 | -------------------------------------------------------------------------------- /libvig/models/externals/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../hardware.h" 4 | 5 | int nanosleep(const struct timespec *req, struct timespec *rem){ 6 | // https://man7.org/linux/man-pages/man2/nanosleep.2.html 7 | // On successfully sleeping for the requested interval, nanosleep() 8 | // returns 0. 9 | time_t sec = req->tv_sec; 10 | long nsec = req->tv_nsec; 11 | TIME += sec*1E9 + nsec; 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /libvig/models/externals/timerfd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int timerfd_create(int clockid, int flags) { 4 | // OK, its usage implies timerfd_gettime/settime and we don't support those 5 | // so we know the app doesn't actually need a timer 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /libvig/models/externals/unistd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "_externals.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | unsigned int sleep(unsigned int seconds) { 14 | // Whatever, code shouldn't use sleep anyway 15 | // If this exposes bugs, great! 16 | return 0; 17 | } 18 | 19 | uid_t getuid(void) { 20 | // No errors: "These functions are always successful." 21 | // -- http://man7.org/linux/man-pages/man2/getuid.2.html 22 | return 0; // We are root! well, we pretend to be, at least 23 | } 24 | 25 | long syscall(long number, ...) { 26 | // 0 is a kernel thing, 1 is init, so let's say 2 27 | if (number == SYS_gettid) { 28 | return 2; 29 | } 30 | 31 | // Not supported! 32 | klee_abort(); 33 | } 34 | 35 | int getpagesize(void) { return PAGE_SIZE; } 36 | 37 | int __syscall_rt_sigaction(int signum, const struct sigaction *act, 38 | struct sigaction *oldact, size_t _something) { 39 | // We don't support signals, so no need to do anything 40 | 41 | // "sigaction() returns 0 on success; on error, -1 is returned, and errno is 42 | // set to indicate the error." 43 | // -- http://man7.org/linux/man-pages/man2/sigaction.2.html 44 | return 0; 45 | } 46 | 47 | int sigaction(int signum, const struct sigaction *act, 48 | struct sigaction *oldact) { 49 | // Same as above 50 | return 0; 51 | } 52 | 53 | 54 | static bool pipe_created = false; 55 | 56 | int pipe(int pipefd[2]) { 57 | // http://man7.org/linux/man-pages/man2/pipe.2.html 58 | 59 | klee_assert(!pipe_created); 60 | pipe_created = true; 61 | 62 | // "The array pipefd is used to return two file descriptors referring to the 63 | // ends of the pipe. 64 | // pipefd[0] refers to the read end of the pipe. 65 | // pipefd[1] refers to the write end of the pipe." 66 | pipefd[0] = STUB_PIPE_FD_READ; 67 | pipefd[1] = STUB_PIPE_FD_WRITE; 68 | 69 | // "On success, zero is returned. On error, -1 is returned, and errno is set 70 | // appropriately." 71 | return 0; 72 | } 73 | 74 | void stub_pipe_write(const void *buf, size_t len) { 75 | // nothing, we don't implement reads so no need for writes 76 | } 77 | -------------------------------------------------------------------------------- /libvig/models/hardware.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "libvig/kernel/nfos_pci.h" 10 | 11 | extern uint64_t TIME; 12 | 13 | struct stub_device { 14 | char *name; 15 | 16 | void *mem; // intercepted by stub 17 | size_t mem_len; 18 | void *mem_shadow; // used as the backing store 19 | 20 | int16_t current_mdi_address; // -1 == none 21 | 22 | int32_t i2c_state; // see i2cctl implementation 23 | uint8_t i2c_counter; // number of bits, N/ACKs included, since the start of 24 | // the current operation 25 | uint8_t i2c_address; // address of the current operation 26 | uint64_t i2c_start_time; // time of last START 27 | uint64_t i2c_clock_time; // time of last clock change 28 | uint64_t i2c_stop_time; // time of last stop 29 | 30 | uint8_t sfp_address; // see i2cctl sfp implementation 31 | 32 | // required for the reset hack... 33 | uint64_t old_mbuf_addr; 34 | 35 | // required for the interrupts file, even though interrupts are not used 36 | // TODO can we remove this, and can we write down how we know interrupts are 37 | // disabled? I think it's a register... 38 | int interrupts_fd; 39 | 40 | // used when resetting 41 | uint32_t initial_rdt; 42 | }; 43 | 44 | #ifdef VIGOR_MODEL_HARDWARE 45 | struct stub_device DEVICES[STUB_DEVICES_COUNT]; 46 | 47 | void stub_hardware_receive_packet(uint16_t device); 48 | // HACK this should not be needed :( but it is cause of the current impl. of 49 | // havocing 50 | void stub_hardware_reset_receive(uint16_t device); 51 | 52 | struct nfos_pci_nic *stub_hardware_get_nics(int *n); 53 | 54 | #else // VIGOR_MODEL_HARDWARE 55 | struct stub_device DEVICES[0]; 56 | 57 | static inline void stub_hardware_receive_packet(uint16_t device) {} 58 | static inline void stub_hardware_reset_receive(uint16_t device) {} 59 | #endif // VIGOR_MODEL_HARDWARE 60 | -------------------------------------------------------------------------------- /libvig/models/kernel/klee/klee.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static inline void klee_make_symbolic(void *addr, size_t nbytes, const char *name) { abort(); } 8 | static inline int klee_range(int begin, int end, const char *name) { abort(); } 9 | static inline int klee_int(const char *name) { abort(); } 10 | __attribute__((noreturn)) static inline void klee_silent_exit(int status) { exit(status); } 11 | __attribute__((noreturn)) static inline void klee_abort(void) { abort(); } 12 | static inline void klee_report_error(const char *file, int line, const char *message, const char *suffix) { abort(); } 13 | static inline size_t klee_get_obj_size(void *ptr) { abort(); } 14 | static inline uintptr_t klee_choose(uintptr_t n) { abort(); } 15 | #define klee_assert assert 16 | static inline unsigned klee_is_symbolic(uintptr_t n) { abort(); } 17 | #define klee_note assert 18 | 19 | static inline int klee_induce_invariants() { abort(); } 20 | static inline void klee_forbid_access(void *ptr, int width, char *message) {} 21 | static inline void klee_allow_access(void *ptr, int width) {} 22 | static inline void klee_possibly_havoc(void *ptr, int width, char *name) {} 23 | -------------------------------------------------------------------------------- /libvig/models/str-descr.h: -------------------------------------------------------------------------------- 1 | #ifndef _STR_DESCR_H_INCLUDED_ 2 | #define _STR_DESCR_H_INCLUDED_ 3 | 4 | // This file is not a model itself, but a definition of types used in models 5 | 6 | struct str_field_descr { 7 | int offset; 8 | int width; 9 | int count; 10 | char *name; 11 | }; 12 | 13 | struct nested_field_descr { 14 | int base_offset; 15 | int offset; 16 | int width; 17 | int count; 18 | char *name; 19 | }; 20 | 21 | struct nested_nested_field_descr { 22 | int base_base_offset; 23 | int base_offset; 24 | int offset; 25 | int width; 26 | char *name; 27 | }; 28 | 29 | #endif //_STR_DESCR_H_INCLUDED_ 30 | -------------------------------------------------------------------------------- /libvig/models/tinynf-env/endian.c: -------------------------------------------------------------------------------- 1 | #include "env/endian.h" 2 | 3 | uint16_t tn_cpu_to_le16(uint16_t val) { return val; } 4 | uint16_t tn_le_to_cpu16(uint16_t val) { return val; } 5 | uint32_t tn_cpu_to_le32(uint32_t val) { return val; } 6 | uint32_t tn_le_to_cpu32(uint32_t val) { return val; } 7 | uint64_t tn_cpu_to_le64(uint64_t val) { return val; } 8 | uint64_t tn_le_to_cpu64(uint64_t val) { return val; } 9 | -------------------------------------------------------------------------------- /libvig/models/tinynf-env/memory.c: -------------------------------------------------------------------------------- 1 | #include "env/memory.h" 2 | 3 | #include 4 | 5 | // TODO symbex linux-x86 env instead - need /dev/mem 6 | 7 | bool tn_mem_allocate(size_t size, void** out_addr) 8 | { 9 | // don't forget about alignment! 10 | uintptr_t addr = (uintptr_t) calloc(1, 2 * size); 11 | *out_addr = (void*) (addr + (size - (addr % size))); 12 | return true; 13 | } 14 | 15 | void tn_mem_free(void* addr) 16 | { 17 | // Do not free due to the alignment issues above, anyway this is only called in cases of failure 18 | } 19 | 20 | bool tn_mem_phys_to_virt(uintptr_t addr, size_t size, void** out_virt_addr) 21 | { 22 | *out_virt_addr = (void*) addr; 23 | return true; 24 | } 25 | 26 | bool tn_mem_virt_to_phys(void* addr, uintptr_t* out_phys_addr) 27 | { 28 | *out_phys_addr = (uintptr_t) addr; 29 | return true; 30 | } 31 | -------------------------------------------------------------------------------- /libvig/models/tinynf-env/pci.c: -------------------------------------------------------------------------------- 1 | #include "env/pci.h" 2 | #include "libvig/models/hardware.h" 3 | 4 | uint32_t tn_pci_read(struct tn_pci_address address, uint8_t reg) 5 | { 6 | if (reg == 0xAAu) { 7 | // DEVICESTATUS 8 | return 0; 9 | } 10 | if (reg == 0x44u) { 11 | // PMCSR 12 | return 0; 13 | } 14 | if (reg == 0x00u) { 15 | // ID 16 | return ((0x10FBu << 16) | 0x8086u); 17 | } 18 | if (reg == 0x10u) { 19 | // BAR0_LOW 20 | return ((uintptr_t) DEVICES[address.function].mem & 0xFFFFFFFF) | 0b100; 21 | } 22 | if (reg == 0x14u) { 23 | // BAR0_HIGH 24 | return ((uintptr_t) DEVICES[address.function].mem >> 32) & 0xFFFFFFFF; 25 | } 26 | // unknown 27 | return (uint32_t) -1; 28 | } 29 | 30 | void tn_pci_write(struct tn_pci_address address, uint8_t reg, uint32_t value) 31 | { 32 | // OK, whatever 33 | // TODO check proper writes 34 | } 35 | -------------------------------------------------------------------------------- /libvig/models/tinynf-env/time.c: -------------------------------------------------------------------------------- 1 | #include "env/time.h" 2 | 3 | #include "libvig/models/hardware.h" 4 | 5 | void tn_sleep_us(uint64_t microseconds) { TIME += microseconds * 1000; } 6 | -------------------------------------------------------------------------------- /libvig/models/verified/cht.c: -------------------------------------------------------------------------------- 1 | #include "klee/klee.h" 2 | #include "libvig/verified/cht.h" 3 | 4 | int cht_fill_cht(struct Vector *cht, uint32_t cht_height, 5 | uint32_t backend_capacity) { 6 | klee_trace_ret(); 7 | klee_trace_param_u64((uint64_t)cht, "cht"); 8 | klee_trace_param_u32(cht_height, "cht_height"); 9 | klee_trace_param_u32(backend_capacity, "backend_capacity"); 10 | // see how long we can run without doing any modelling here 11 | return klee_int("cht_fill_cht_successful"); 12 | } 13 | 14 | int cht_find_preferred_available_backend(uint64_t hash, struct Vector *cht, 15 | struct DoubleChain *active_backends, 16 | uint32_t cht_height, 17 | uint32_t backend_capacity, 18 | int *chosen_backend) { 19 | klee_trace_ret(); 20 | klee_trace_param_u64(hash, "hash"); 21 | klee_trace_param_u64((uint64_t)cht, "cht"); 22 | klee_trace_param_u64((uint64_t)active_backends, "active_backends"); 23 | klee_trace_param_u32(cht_height, "cht_height"); 24 | klee_trace_param_u32(backend_capacity, "backend_capacity"); 25 | klee_trace_param_ptr(chosen_backend, sizeof(int), "chosen_backend"); 26 | if (klee_int("prefered_backend_found")) { 27 | *chosen_backend = klee_int("chosen_backend"); 28 | klee_assume(0 <= *chosen_backend); 29 | klee_assume(*chosen_backend < backend_capacity); 30 | return 1; 31 | } else { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libvig/models/verified/double-chain-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _DOUBLE_CHAIN_STUB_CONTROL_H_INCLUDED_ 2 | #define _DOUBLE_CHAIN_STUB_CONTROL_H_INCLUDED_ 3 | 4 | #include "libvig/verified/double-chain.h" 5 | 6 | void dchain_make_space(struct DoubleChain *chain, int nfreed); 7 | 8 | void dchain_reset(struct DoubleChain *chain, int index_range); 9 | 10 | #endif //_DOUBLE_CHAIN_STUB_CONTROL_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /libvig/models/verified/double-map-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _DOUBLE_MAP_STUB_CONTROL_H_INCLUDED_ 2 | #define _DOUBLE_MAP_STUB_CONTROL_H_INCLUDED_ 3 | #include "libvig/models/str-descr.h" 4 | 5 | typedef int entry_condition(void *key_a, void *key_b, int index, void *value, 6 | void *state); 7 | 8 | struct DoubleMap; 9 | 10 | void dmap_set_entry_condition(struct DoubleMap *map, entry_condition *cond, 11 | void *state); 12 | //@ requires true; 13 | //@ ensures true; 14 | 15 | void dmap_set_layout(struct DoubleMap *map, 16 | struct str_field_descr *key_a_fields, int key_a_count, 17 | struct str_field_descr *key_b_fields, int key_b_count, 18 | struct str_field_descr *value_fields, int value_count, 19 | struct nested_field_descr *value_nested_fields, 20 | int val_nests_count); 21 | void dmap_reset(struct DoubleMap *map, int capacity); 22 | 23 | #endif //_DOUBLE_MAP_STUB_CONTROL_H_INCLUDED_ 24 | -------------------------------------------------------------------------------- /libvig/models/verified/ether.c: -------------------------------------------------------------------------------- 1 | #include "ether.h" 2 | 3 | #include 4 | 5 | struct str_field_descr rte_ether_addr_descrs[] = { 6 | {offsetof(struct rte_ether_addr, addr_bytes), sizeof(uint8_t ), 6, "addr_bytes"}, 7 | }; 8 | struct nested_field_descr rte_ether_addr_nests[] = { 9 | 10 | }; 11 | unsigned rte_ether_addr_hash(void* obj) 12 | { 13 | klee_trace_ret(); 14 | klee_trace_param_tagged_ptr(obj, sizeof(struct rte_ether_addr), 15 | "obj", "rte_ether_addr", TD_BOTH); 16 | for (int i = 0; i < sizeof(rte_ether_addr_descrs)/sizeof(rte_ether_addr_descrs[0]); ++i) { 17 | klee_trace_param_ptr_field_arr_directed(obj, 18 | rte_ether_addr_descrs[i].offset, 19 | rte_ether_addr_descrs[i].width, 20 | rte_ether_addr_descrs[i].count, 21 | rte_ether_addr_descrs[i].name, 22 | TD_BOTH); 23 | } 24 | for (int i = 0; i < sizeof(rte_ether_addr_nests)/sizeof(rte_ether_addr_nests[0]); ++i) { 25 | klee_trace_param_ptr_nested_field_arr_directed(obj, 26 | rte_ether_addr_nests[i].base_offset, 27 | rte_ether_addr_nests[i].offset, 28 | rte_ether_addr_nests[i].width, 29 | rte_ether_addr_nests[i].count, 30 | rte_ether_addr_nests[i].name, 31 | TD_BOTH); 32 | } 33 | return klee_int("rte_ether_addr_hash"); 34 | } 35 | -------------------------------------------------------------------------------- /libvig/models/verified/ether.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "libvig/verified/ether.h" 4 | #include "libvig/models/str-descr.h" 5 | 6 | extern struct str_field_descr rte_ether_addr_descrs[1]; 7 | extern struct nested_field_descr rte_ether_addr_nests[0]; 8 | -------------------------------------------------------------------------------- /libvig/models/verified/expirator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libvig/verified/expirator.h" 3 | #include "double-chain-control.h" 4 | 5 | int expire_items(struct DoubleChain *chain, struct DoubleMap *map, 6 | vigor_time_t time) { 7 | klee_trace_ret(); 8 | klee_trace_param_u64((uint64_t)chain, "chain"); 9 | klee_trace_param_u64((uint64_t)map, "map"); 10 | klee_trace_param_i64(time, "exp_time"); 11 | int nfreed = klee_int("number_of_freed_flows"); 12 | klee_assume(0 <= nfreed); 13 | dchain_make_space(chain, nfreed); 14 | // Tell dchain model that we freed some indexes here 15 | return nfreed; 16 | } 17 | 18 | int expire_items_single_map(struct DoubleChain *chain, struct Vector *vector, 19 | struct Map *map, vigor_time_t time) { 20 | klee_trace_ret(); 21 | klee_trace_param_u64((uint64_t)chain, "chain"); 22 | klee_trace_param_u64((uint64_t)vector, "vector"); 23 | klee_trace_param_u64((uint64_t)map, "map"); 24 | klee_trace_param_i64(time, "time"); 25 | int nfreed = klee_int("unmber_of_freed_flows"); 26 | klee_assume(0 <= nfreed); 27 | dchain_make_space(chain, nfreed); 28 | return nfreed; 29 | } 30 | -------------------------------------------------------------------------------- /libvig/models/verified/lpm-dir-24-8-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _LPM_DIR_24_8_STUB_CONTROL_H_INCLUDED_ 2 | #define _LPM_DIR_24_8_STUB_CONTROL_H_INCLUDED_ 3 | 4 | typedef bool lpm_entry_condition(uint32_t prefix, int value); 5 | 6 | void lpm_set_entry_condition(struct lpm *lpm, lpm_entry_condition *cond); 7 | 8 | #endif// _LPM_DIR_24_8_STUB_CONTROL_H_INCLUDED_ 9 | -------------------------------------------------------------------------------- /libvig/models/verified/lpm-dir-24-8.c: -------------------------------------------------------------------------------- 1 | #include "klee/klee.h" 2 | #include 3 | #include 4 | #include 5 | #include "libvig/verified/lpm-dir-24-8.h" 6 | #include "lpm-dir-24-8-control.h" 7 | 8 | #define PREALLOC_SIZE (256) 9 | #define NUM_ELEMS (3) 10 | 11 | struct lpm { 12 | lpm_entry_condition *cond; 13 | }; 14 | 15 | int lpm_allocate(struct lpm **lpm_out) { 16 | klee_trace_ret(); 17 | klee_trace_param_ptr(lpm_out, sizeof(struct lpm*), "lpm_out"); 18 | int allocation_succeeded = klee_int("lpm_alloc_success"); 19 | if (!allocation_succeeded) 20 | return 0; 21 | *lpm_out = malloc(sizeof(struct lpm)); 22 | (**lpm_out).cond = NULL; 23 | return 1; 24 | } 25 | 26 | void lpm_free(struct lpm *lpm) { 27 | klee_assert(0);//Not supported 28 | } 29 | 30 | int lpm_update_elem(struct lpm *lpm, uint32_t prefix, 31 | uint8_t prefixlen, uint16_t value) { 32 | klee_trace_ret(); 33 | klee_trace_param_u64((uint64_t)lpm, "lpm"); 34 | klee_trace_param_u32(prefix, "prefix"); 35 | //VV should be u8, but too lazy to add that to klee 36 | klee_trace_param_u16(prefixlen, "prefixlen"); 37 | klee_trace_param_u16(value, "value"); 38 | return klee_int("lpm_update_elem_result"); 39 | } 40 | 41 | int lpm_lookup_elem(struct lpm *lpm, uint32_t prefix) { 42 | klee_trace_ret(); 43 | klee_trace_param_u64((uint64_t)lpm, "lpm"); 44 | klee_trace_param_u32(prefix, "prefix"); 45 | int val = klee_int("lpm_lookup_reesult"); 46 | if (lpm->cond) 47 | klee_assume(lpm->cond(prefix, val)); 48 | return val; 49 | } 50 | 51 | void lpm_set_entry_condition(struct lpm *lpm, lpm_entry_condition *cond) { 52 | lpm->cond = cond; 53 | } 54 | -------------------------------------------------------------------------------- /libvig/models/verified/map-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAP_STUB_CONTROL_H_INCLUDED_ 2 | #define _MAP_STUB_CONTROL_H_INCLUDED_ 3 | 4 | #include "libvig/verified/map.h" 5 | #include "libvig/models/str-descr.h" 6 | 7 | #include 8 | 9 | #define PREALLOC_SIZE (256) 10 | #define NUM_ELEMS (3) 11 | 12 | typedef bool map_entry_condition(void *key, int value, void* state); 13 | 14 | struct Map { 15 | void *keyp[NUM_ELEMS]; 16 | void *key_copyp[NUM_ELEMS]; 17 | char key_copies[NUM_ELEMS * PREALLOC_SIZE]; 18 | int unallocated_start; 19 | int allocated_index[NUM_ELEMS]; 20 | int key_deleted[NUM_ELEMS]; 21 | int next_unclaimed_entry; 22 | int capacity; 23 | int occupancy; 24 | int backup_occupancy; 25 | int has_layout; 26 | int key_size; 27 | int key_field_count; 28 | int nested_key_field_count; 29 | map_entry_condition *ent_cond; 30 | void *ent_cond_state; 31 | struct str_field_descr key_fields[PREALLOC_SIZE]; 32 | struct nested_field_descr key_nests[PREALLOC_SIZE]; 33 | char *key_type; 34 | map_keys_equality *keq; 35 | 36 | int Num_bucket_traversals; 37 | int Num_hash_collisions; 38 | }; 39 | 40 | void map_set_layout(struct Map *map, struct str_field_descr *key_fields, 41 | int key_fields_count, struct nested_field_descr *key_nests, 42 | int nested_key_fields_count, char *key_type); 43 | 44 | void map_set_entry_condition(struct Map *map, map_entry_condition *cond, 45 | void* state); 46 | 47 | void map_reset(struct Map *map); 48 | 49 | #endif //_MAP_STUB_CONTROL_H_INCLUDED_ 50 | -------------------------------------------------------------------------------- /libvig/models/verified/packet-io-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _PACKET_IO_STUB_CONTROL_H_INCLUDED_ 2 | #define _PACKET_IO_STUB_CONTROL_H_INCLUDED_ 3 | #include "libvig/models/str-descr.h" 4 | 5 | // kinda stupid place for this but it has to be somewhere 6 | #include 7 | #include 8 | #include "libvig/verified/tcpudp_hdr.h" 9 | #define MBUF_MIN_SIZE (sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct tcpudp_hdr)) 10 | 11 | typedef bool (*chunk_constraint)(void *); 12 | 13 | void set_packet_receive_success(bool received); 14 | 15 | void packet_set_next_chunk_layout(void *p, uint32_t length, 16 | struct str_field_descr *fields, int n_fields, 17 | struct nested_field_descr *nests, int n_nests, 18 | const char *tname); 19 | 20 | bool packet_is_last_borrowed_chunk(void *p, void *chunk); 21 | 22 | #endif //_PACKET_IO_STUB_CONTROL_H_INCLUDED_ 23 | -------------------------------------------------------------------------------- /libvig/models/verified/vector-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _VECTOR_STUB_CONTROL_H_INCLUDED_ 2 | #define _VECTOR_STUB_CONTROL_H_INCLUDED_ 3 | 4 | #include "libvig/verified/vector.h" 5 | #include "libvig/models/str-descr.h" 6 | 7 | #include 8 | 9 | void vector_set_layout(struct Vector *vector, 10 | struct str_field_descr *value_fields, int field_count, 11 | struct nested_field_descr *val_nest_fields, 12 | int nest_field_count, char *type_tag); 13 | 14 | typedef bool vector_entry_condition(void *value, int index, void *state); 15 | 16 | void vector_set_entry_condition(struct Vector *vector, 17 | vector_entry_condition *cond, void *state); 18 | 19 | void vector_reset(struct Vector *vector); 20 | 21 | #endif // _VECTOR_STUB_CONTROL_H_INCLUDED_ 22 | -------------------------------------------------------------------------------- /libvig/models/verified/vigor-time-control.h: -------------------------------------------------------------------------------- 1 | #ifndef _MY_TIME_STUB_CONTROL_H_INCLUDED_ 2 | #define _MY_TIME_STUB_CONTROL_H_INCLUDED_ 3 | 4 | #include 5 | #include "libvig/verified/vigor-time.h" 6 | 7 | vigor_time_t start_time(void); 8 | //@ requires true; 9 | //@ ensures result >= 0 &*& last_time(result); 10 | 11 | vigor_time_t restart_time(void); 12 | 13 | vigor_time_t get_start_time(void); 14 | 15 | #endif //_MY_TIME_STUB_CONTROL_H_INCLUDED_ 16 | -------------------------------------------------------------------------------- /libvig/models/verified/vigor-time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef KLEE_VERIFICATION 5 | # include 6 | #endif 7 | #include "vigor-time-control.h" 8 | 9 | vigor_time_t starting_time = 0; 10 | vigor_time_t last_time = 0; 11 | 12 | #ifdef KLEE_VERIFICATION 13 | 14 | __attribute__((noinline)) vigor_time_t start_time(void) { 15 | klee_trace_ret(); 16 | vigor_time_t starting_time; 17 | klee_make_symbolic(&starting_time, sizeof(vigor_time_t), "starting_time"); 18 | klee_assume(0 <= starting_time); 19 | 20 | last_time = starting_time; 21 | return last_time; 22 | } 23 | 24 | vigor_time_t restart_time(void) { 25 | klee_make_symbolic(&starting_time, sizeof(vigor_time_t), "restarting_time"); 26 | klee_assume(0 <= starting_time); 27 | last_time = starting_time; 28 | return last_time; 29 | } 30 | 31 | __attribute__((noinline)) vigor_time_t current_time(void) { 32 | klee_trace_ret(); 33 | vigor_time_t next_time; 34 | klee_make_symbolic(&next_time, sizeof(vigor_time_t), "next_time"); 35 | klee_assume(last_time <= next_time); 36 | last_time = next_time; 37 | return next_time; 38 | } 39 | 40 | #else // KLEE_VERIFICATION 41 | 42 | vigor_time_t restart_time(void) { assert(0); } 43 | 44 | #endif // KLEE_VERIFICATION 45 | 46 | vigor_time_t get_start_time_internal(void) { return starting_time; } 47 | 48 | vigor_time_t get_start_time(void) { return get_start_time_internal(); } 49 | 50 | vigor_time_t recent_time(void) { 51 | // Don't trace this function, it only reterns the last result of current_time 52 | return last_time; 53 | } 54 | -------------------------------------------------------------------------------- /libvig/proof/arith.gh: -------------------------------------------------------------------------------- 1 | #ifndef ARITH_GH 2 | #define ARITH_GH 3 | 4 | lemma void mul_equal(int a, int b, int c); 5 | requires 0 <= a &*& 0 < b &*& 0 <= c &*& a * b == c; 6 | ensures a <= c; 7 | 8 | //Taken from https://groups.google.com/forum/#!topic/verifast/eCf5QzLtGlo 9 | lemma void mul_nonnegative(int x, int y); 10 | requires 0 <= x &*& 0 <= y; 11 | ensures 0 <= x * y; 12 | 13 | lemma void mul_mono(int i, int n, int a); 14 | requires i <= n &*& 0 <= a; 15 | ensures i * a <= n * a; 16 | 17 | lemma void mul_nonzero(int a, int b); 18 | requires 0 < a &*& 0 < b; 19 | ensures 0 < a * b; 20 | 21 | lemma void mul_mono_strict(int i, int n, int a); 22 | requires 0 <= i &*& i < n &*& 0 < a; 23 | ensures 0 <= i * a &*& i * a < n * a; 24 | 25 | lemma void mul_bounds(int x, int xb, int y, int yb); 26 | requires 0 <= x &*& x <= xb &*& 0 <= y &*& y <= yb; 27 | ensures 0 <= x * y &*& x * y <= xb * yb; 28 | 29 | lemma void mul_subst(int x, int y, int a); 30 | requires x == y &*& 0 <= a; 31 | ensures a*x == a*y; 32 | 33 | lemma void less_than_neq(int a, int b); 34 | requires a < b; 35 | ensures a != b; 36 | 37 | #endif ARITH_GH 38 | -------------------------------------------------------------------------------- /libvig/proof/boilerplate-assumptions.c: -------------------------------------------------------------------------------- 1 | #include "../verified/boilerplate-util.h" 2 | 3 | // The following functions are by no means implementations of a CRC32 hash 4 | // algorithm, but rather are stubs marking our assumption about correctness of 5 | // the implementation of a builtin hash functions that map into x86 6 | // instructions. 7 | unsigned __builtin_ia32_crc32si(unsigned acc, unsigned int x) 8 | /*@ requires true; @*/ 9 | /*@ ensures result == crc32_hash(acc, x); @*/ 10 | { 11 | //@ assume(false); 12 | return x; 13 | } 14 | unsigned long long __builtin_ia32_crc32di(unsigned long long acc, 15 | unsigned long long x) 16 | /*@ requires true; @*/ 17 | /*@ ensures result == crc32_hash(acc, x); @*/ 18 | { 19 | //@ assume(false); 20 | return x; 21 | } 22 | -------------------------------------------------------------------------------- /libvig/proof/mod-pow2.gh: -------------------------------------------------------------------------------- 1 | #ifndef _MOD_POW2_INCLUDED_ 2 | #define _MOD_POW2_INCLUDED_ 3 | 4 | #include 5 | 6 | #include "modulo.gh" 7 | 8 | fixpoint int pow2(nat m) { 9 | return pow_nat(2, m); 10 | } 11 | 12 | fixpoint option is_pow2(int x, nat m) { 13 | switch(m) { 14 | case zero: return (x == pow2(zero) ? some(zero) : none); 15 | case succ(m_pred): return (x == pow2(m) ? some(m) : is_pow2(x, m_pred)); 16 | } 17 | } 18 | 19 | lemma nat is_pow2_some(int x, nat m); 20 | requires is_pow2(x, m) != none; 21 | ensures x == pow2(result) &*& int_of_nat(result) <= int_of_nat(m); 22 | 23 | lemma void some_is_pow2(int x, nat n, nat m); 24 | requires x == pow2(n) &*& int_of_nat(n) <= int_of_nat(m); 25 | ensures is_pow2(x, m) == some(n); 26 | 27 | lemma void mod_bitand_equiv(int k, int capacity, nat m); 28 | requires 0 <= k &*& k < pow2(N32) &*& 0 < capacity &*& capacity < INT_MAX &*& capacity == pow2(m) &*& int_of_nat(m) < 32; 29 | ensures (k % capacity) == (k & (capacity - 1)) &*& (k % capacity) == loop_fp(k, capacity); 30 | 31 | lemma void check_pow2_valid(int x); 32 | requires 0 < x &*& x < pow2(N32) &*& (x & (x - 1)) == 0; 33 | ensures is_pow2(x, N31) != none; 34 | 35 | #endif //_MOD_POW2_GH_INCLUDED_ 36 | -------------------------------------------------------------------------------- /libvig/proof/transpose.gh: -------------------------------------------------------------------------------- 1 | #ifndef _TRANSPOSE_GH_INCLUDED_ 2 | #define _TRANSPOSE_GH_INCLUDED_ 3 | 4 | #include 5 | #include "arith.gh" 6 | #include "modulo.gh" 7 | #include "listutils.gh" 8 | 9 | // ------------- extract_row/column ------------- 10 | 11 | fixpoint int extract_row(int nb_cols, int n) { 12 | return n / nb_cols; 13 | } 14 | 15 | fixpoint int extract_col(int nb_cols, int n) { 16 | return n % nb_cols; 17 | } 18 | // ------------- transpose ------------- 19 | 20 | fixpoint list transpose_rec_row(list xs, int nb_cols, int idx_col_cst, int idx_row, nat it_row) { 21 | switch(it_row) { 22 | case zero: return nil; 23 | case succ(it_row_pred): return cons( nth(idx_row * nb_cols + idx_col_cst, xs) , transpose_rec_row(xs, nb_cols, idx_col_cst, idx_row + 1, it_row_pred) ); 24 | } 25 | } 26 | 27 | fixpoint list transpose_rec(list xs, int nb_rows, int nb_cols, int idx_col, nat it_col) { 28 | switch(it_col) { 29 | case zero: return nil; 30 | case succ(it_col_pred): return append( transpose_rec_row(xs, nb_cols, idx_col, 0, nat_of_int(nb_rows)) , transpose_rec(xs, nb_rows, nb_cols, idx_col + 1, it_col_pred) ); 31 | } 32 | } 33 | 34 | fixpoint list transpose(list xs, int nb_rows, int nb_cols) { 35 | return transpose_rec(xs, nb_rows, nb_cols, 0, nat_of_int(nb_cols)); 36 | } 37 | 38 | // ------------- transpose with filter_idx and count ------------- 39 | 40 | fixpoint bool eq_idx_transpose(int nb_rows, int nb_cols, int x, int y) { 41 | return y == extract_col(nb_cols, x) * nb_rows + extract_row(nb_cols, x); 42 | } 43 | 44 | fixpoint list gen_idx_transpose(list idxs, int nb_rows, int nb_cols) { 45 | switch(idxs) { 46 | case nil: return nil; 47 | case cons(i0, is0): return cons(extract_col(nb_cols, i0) * nb_rows + extract_row(nb_cols, i0), gen_idx_transpose(is0, nb_rows, nb_cols)); 48 | } 49 | } 50 | 51 | #endif //_LISTUTILS_GH_INCLUDED_ 52 | -------------------------------------------------------------------------------- /libvig/verified/map-util.h: -------------------------------------------------------------------------------- 1 | #ifndef _MAP_UTIL_H_INCLUDED_ 2 | #define _MAP_UTIL_H_INCLUDED_ 3 | 4 | #define CAPACITY_UPPER_LIMIT 140000 5 | 6 | #include 7 | 8 | // Here the return type of a hash function is assumed to be unsigned = uint32_t 9 | // If you want to change it, you can run find&replace for unsigned in map 10 | // related files, just be careful not to mix the type of the map capacity and 11 | // a couple of other unsigned unrelated values. 12 | 13 | typedef unsigned map_key_hash/*@ (predicate (void*; K) keyp, 14 | fixpoint (K, unsigned) hash) @*/(void* k1); 15 | //@ requires [?fr]keyp(k1, ?kk1); 16 | //@ ensures [fr]keyp(k1, kk1) &*& result == hash(kk1); 17 | 18 | typedef bool map_keys_equality/*@(predicate (void*; K) keyp) @*/(void* k1, void* k2); 19 | //@ requires [?fr1]keyp(k1, ?kk1) &*& [?fr2]keyp(k2, ?kk2); 20 | //@ ensures [fr1]keyp(k1, kk1) &*& [fr2]keyp(k2, kk2) &*& (false == result ? (kk1 != kk2) : (kk1 == kk2)); 21 | 22 | #endif//_MAP_UTIL_H_INCLUDED_ 23 | -------------------------------------------------------------------------------- /libvig/verified/tcpudp_hdr.h: -------------------------------------------------------------------------------- 1 | #ifndef _TCPUDP_H_INCLUDED_ 2 | #define _TCPUDP_H_INCLUDED_ 3 | 4 | #include 5 | 6 | struct tcpudp_hdr { 7 | uint16_t src_port; 8 | uint16_t dst_port; 9 | } 10 | #ifdef _NO_VERIFAST_ 11 | __attribute__((__packed__)) // VeriFast does not understand attributes 12 | #endif //_NO_VERIFAST_ 13 | ; 14 | 15 | #endif //_TCPUDP_H_INCLUDED_ 16 | -------------------------------------------------------------------------------- /libvig/verified/vigor-time.c: -------------------------------------------------------------------------------- 1 | #include "vigor-time.h" 2 | 3 | #include 4 | #include 5 | 6 | #ifdef NFOS 7 | # include 8 | #endif 9 | 10 | vigor_time_t last_time = 0; 11 | 12 | #ifdef NFOS 13 | time_t time(time_t *timer) { assert(0); } 14 | 15 | int clock_gettime(clockid_t clk_id, struct timespec *tp) { 16 | // Others not implemented 17 | if (clk_id != CLOCK_MONOTONIC && clk_id != CLOCK_MONOTONIC_RAW) { 18 | return -1; 19 | } 20 | 21 | __uint128_t tsc = nfos_rdtsc(); 22 | __uint128_t freq = nfos_tsc_get_freq(); 23 | uint64_t time_ns = (uint64_t)(tsc * 1000000000ul / freq); 24 | 25 | # ifdef KLEE_VERIFICATION 26 | // HACK: Verifast doesn't like the division 27 | // even though there is no reason why it shouldn't 28 | // be correct since it's just scaling the TSC by a constant. 29 | // Maybe some more lemmas are needed. 30 | tp->tv_sec = tsc / freq; 31 | tp->tv_nsec = tsc; // FIXME: modulo 1000000000, etc, use a proper formula; 32 | # else // KLEE_VERIFICATION 33 | tp->tv_nsec = time_ns % 1000000000ul; 34 | tp->tv_sec = time_ns / 1000000000ul; 35 | # endif // KLEE_VERIFICATION 36 | 37 | return 0; 38 | } 39 | 40 | int gettimeofday(struct timeval *tv, void* tz) 41 | { 42 | if (tz != NULL) { 43 | return -1; 44 | } 45 | struct timespec tp; 46 | int ret = clock_gettime(CLOCK_MONOTONIC, &tp); 47 | if (ret != 0) { 48 | return ret; 49 | } 50 | tv->tv_sec = tp.tv_sec; 51 | tv->tv_usec = tp.tv_nsec / 1000; 52 | return 0; 53 | } 54 | #endif 55 | 56 | vigor_time_t current_time(void) 57 | //@ requires last_time(?x); 58 | //@ ensures result >= 0 &*& x <= result &*& last_time(result); 59 | { 60 | struct timespec tp; 61 | clock_gettime(CLOCK_MONOTONIC, &tp); 62 | last_time = tp.tv_sec * 1000000000ul + tp.tv_nsec; 63 | return last_time; 64 | } 65 | 66 | vigor_time_t recent_time(void) { return last_time; } 67 | -------------------------------------------------------------------------------- /libvig/verified/vigor-time.h: -------------------------------------------------------------------------------- 1 | #ifndef NF_TIME_H_INCLUDED 2 | #define NF_TIME_H_INCLUDED 3 | #include 4 | 5 | // TODO use time_t from time.h - but this is used by VeriFast 6 | // so even #ifdef-ing the time.h inclusion out doesn't work 7 | #define vigor_time_t int64_t 8 | #define PRVIGT PRId64 9 | 10 | #define VIGOR_TIME_SECONDS_MULTIPLIER (1000000000ul) 11 | 12 | //@ predicate last_time(vigor_time_t t); 13 | 14 | // A wrapper around the system time function. Returns the number of 15 | // nanoseconds since the Epoch (1970-01-01 00:00:00 +0000 (UTC)). 16 | // @returns the number of nanoseconds since Epoch. 17 | vigor_time_t current_time(void); 18 | //@ requires last_time(?x); 19 | //@ ensures result >= 0 &*& x <= result &*& last_time(result); 20 | 21 | // Returns the last result of current_time. must only be called after 22 | // current_time was invoked at least once. 23 | vigor_time_t recent_time(void); 24 | 25 | #endif // NF_TIME_H_INCLUDED 26 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(start) 2 | 3 | SECTIONS 4 | { 5 | . = 1M; 6 | 7 | .boot ALIGN(4K) : 8 | { 9 | *(.multiboot_header) 10 | } 11 | 12 | .text ALIGN(4K) : 13 | { 14 | *(.text) 15 | 16 | KEEP(*(.init)) 17 | KEEP(*(.fini)) 18 | 19 | /* .ctors */ 20 | *crtbegin.o(.ctors) 21 | *crtbegin?.o(.ctors) 22 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 23 | *(SORT(.ctors.*)) 24 | *(.ctors) 25 | 26 | /* .dtors */ 27 | *crtbegin.o(.dtors) 28 | *crtbegin?.o(.dtors) 29 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 30 | *(SORT(.dtors.*)) 31 | *(.dtors) 32 | } 33 | 34 | .data ALIGN(4K) : 35 | { 36 | *(.data) 37 | } 38 | 39 | .rodata ALIGN(4K) : 40 | { 41 | *(.rodata) 42 | } 43 | 44 | .bss ALIGN(4K) : 45 | { 46 | *(.bss) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /moonpol/Makefile: -------------------------------------------------------------------------------- 1 | NF_PROCESS_NAME := libmoon 2 | 3 | NF_LAYER := 3 4 | 5 | MOONPOL_ARGS := -b 1 6 | ifeq (true,$(VIGOR_USE_BATCH)) 7 | MOONPOL_ARGS := -b 32 8 | endif 9 | 10 | # for substitutions into dpdk-conf.lua in run: 11 | dquote := " 12 | space := $(null) # 13 | comma := , 14 | 15 | compile: 16 | @echo 'No compile needed!' 17 | 18 | run: 19 | @rm -f /tmp/dpdk-conf.lua 20 | @if [ '$(NF_DPDK_ARGS)' != '' ]; \ 21 | then echo 'DPDKConfig { cli = { "$(subst $(space),$(dquote)$(comma)$(dquote),$(NF_DPDK_ARGS))" } }' \ 22 | > /tmp/dpdk-conf.lua; \ 23 | fi 24 | @# OK if the file doesn't exist, libmoon just ignores it 25 | @sudo $(VIGOR_DIR)/libmoon/build/libmoon policer.lua \ 26 | --dpdk-config=/tmp/dpdk-conf.lua \ 27 | $(MOONPOL_ARGS) 28 | 29 | 30 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 31 | -------------------------------------------------------------------------------- /moonpol/config-generator.c: -------------------------------------------------------------------------------- 1 | // Compile without any special flags, run, redirect output to a file, 2 | // and you have your config (which we provide pre-generated) 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | for (uint32_t n = 0; n <= 65535; n++) { 10 | struct in_addr ip; 11 | ip.s_addr = htonl(n); 12 | printf("%s/32\t15000000\n", inet_ntoa(ip)); 13 | } 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /nf-log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef KLEE_VERIFICATION 4 | # define NF_INFO(text, ...) 5 | #else // KLEE_VERIFICATION 6 | # include 7 | # include 8 | # define NF_INFO(text, ...) \ 9 | printf(text "\n", ##__VA_ARGS__); \ 10 | fflush(stdout); 11 | #endif // KLEE_VERIFICATION 12 | 13 | #ifdef ENABLE_LOG 14 | # include 15 | # include 16 | # define NF_DEBUG(text, ...) \ 17 | fprintf(stderr, "DEBUG: " text "\n", ##__VA_ARGS__); \ 18 | fflush(stderr); 19 | #else // ENABLE_LOG 20 | # define NF_DEBUG(...) 21 | #endif // ENABLE_LOG 22 | -------------------------------------------------------------------------------- /nf-parse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | bool nf_parse_etheraddr(const char* str, struct rte_ether_addr* addr) 10 | { 11 | return sscanf(str, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", 12 | addr->addr_bytes + 0, 13 | addr->addr_bytes + 1, 14 | addr->addr_bytes + 2, 15 | addr->addr_bytes + 3, 16 | addr->addr_bytes + 4, 17 | addr->addr_bytes + 5) 18 | == 6; 19 | } 20 | 21 | bool nf_parse_ipv4addr(const char* str, uint32_t* addr) 22 | { 23 | uint8_t a, b, c, d; 24 | if (sscanf(str, "%hhu.%hhu.%hhu.%hhu", &a, &b, &c, &d) == 4) { 25 | *addr = ((uint32_t) a << 24) | 26 | ((uint32_t) b << 16) | 27 | ((uint32_t) c << 8) | 28 | ((uint32_t) d << 0); 29 | return true; 30 | } 31 | return false; 32 | } 33 | -------------------------------------------------------------------------------- /nf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "libvig/verified/vigor-time.h" 7 | 8 | #define FLOOD_FRAME ((uint16_t) -1) 9 | 10 | struct nf_config; 11 | 12 | bool nf_init(void); 13 | int nf_process(uint16_t device, uint8_t* buffer, uint16_t packet_length, vigor_time_t now); 14 | 15 | extern struct nf_config config; 16 | void nf_config_init(int argc, char **argv); 17 | void nf_config_usage(void); 18 | void nf_config_print(void); 19 | 20 | #ifdef KLEE_VERIFICATION 21 | void nf_loop_iteration_border(unsigned lcore_id, vigor_time_t time); 22 | #endif 23 | -------------------------------------------------------------------------------- /pxe-boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | if [ -z "${1:-}" ]; then 6 | echo "Usage: $(basename $0) " 7 | exit 1 8 | fi 9 | BOOT_IMG="$(realpath ${1:-})" 10 | 11 | PXE_NIC="em4" 12 | PXE_SERVER_IP="192.168.0.254" 13 | PXE_NETWORK_MASK="255.255.255.0" 14 | PXE_CLIENT_IP_FIRST="192.168.0.1" 15 | PXE_CLIENT_IP_LAST="192.168.0.253" 16 | IPXE_IMG="/usr/lib/ipxe/undionly.kpxe" 17 | 18 | if [ ! -f "$IPXE_IMG" ]; then 19 | echo "iPXE not found." 20 | exit 1 21 | fi 22 | 23 | TFTP_DIR="$(mktemp -d)" 24 | ln -s $IPXE_IMG $TFTP_DIR 25 | ln -s $BOOT_IMG $TFTP_DIR 26 | 27 | sudo ifconfig $PXE_NIC $PXE_SERVER_IP netmask $PXE_NETWORK_MASK up 28 | 29 | function cleanup { 30 | echo "Cleaning up." 31 | rm -rf "$TFTP_DIR" 32 | } 33 | trap cleanup EXIT 34 | 35 | echo "Running DHCP/BootP server. Press Ctrl-C when done." 36 | sudo dnsmasq -k -d -l /dev/null -R -h -i $PXE_NIC --bind-dynamic \ 37 | --dhcp-range=$PXE_CLIENT_IP_FIRST,$PXE_CLIENT_IP_LAST,$PXE_NETWORK_MASK,5m \ 38 | --dhcp-userclass=set:ipxe,iPXE \ 39 | --dhcp-boot $(basename $IPXE_IMG) \ 40 | --dhcp-boot tag:ipxe,$(basename $BOOT_IMG) \ 41 | --enable-tftp --tftp-root $TFTP_DIR 42 | -------------------------------------------------------------------------------- /setup/dpdk.config.patch: -------------------------------------------------------------------------------- 1 | --- a/config/common_base 2020-05-26 15:41:39.000000000 +0000 2 | +++ b/config/common_base 2020-07-23 10:17:36.120975853 +0000 3 | @@ -42,7 +42,8 @@ 4 | # 5 | # Use intrinsics or assembly code for key routines 6 | # 7 | -CONFIG_RTE_FORCE_INTRINSICS=n 8 | +# Enable atomic intrinsics, otherwise it uses inline ASM which KLEE doesn't support 9 | +CONFIG_RTE_FORCE_INTRINSICS=y 10 | 11 | # 12 | # Machine forces strict alignment constraints. 13 | -------------------------------------------------------------------------------- /setup/dpdk.ixgbe.avoid_bit_bang.patch: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c 2 | index b1b39eb..b09957f 100644 3 | --- a/drivers/net/ixgbe/ixgbe_ethdev.c 4 | +++ b/drivers/net/ixgbe/ixgbe_ethdev.c 5 | @@ -1216,6 +1216,9 @@ eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev) 6 | } 7 | hw->fc.send_xon = 1; 8 | 9 | + /* Initialize the params, so we don't bit-bang for the checksum if we don't need to */ 10 | + hw->eeprom.ops.init_params(hw); 11 | + 12 | /* Make sure we have a good EEPROM before we read from it */ 13 | diag = ixgbe_validate_eeprom_checksum(hw, &csum); 14 | if (diag != IXGBE_SUCCESS) { 15 | -------------------------------------------------------------------------------- /setup/dpdk.ixgbe.rdrxctl_special_writes.patch: -------------------------------------------------------------------------------- 1 | --- a/drivers/net/ixgbe/ixgbe_rxtx.c 2020-09-03 06:50:58.737244902 +0000 2 | +++ b/drivers/net/ixgbe/ixgbe_rxtx.c 2020-09-07 10:12:58.934164416 +0000 3 | @@ -5169,6 +5169,12 @@ 4 | else 5 | rdrxctl |= IXGBE_RDRXCTL_CRCSTRIP; 6 | rdrxctl &= ~IXGBE_RDRXCTL_RSCFRSTSIZE; 7 | + // Section 8.2.3.8.8 8 | + // "Software should set [RSCACKC, bit 25] to 1" 9 | + rdrxctl |= IXGBE_RDRXCTL_RSCACKC; 10 | + // "Software should set [FCOE_WRFIX, bit 26] to 1" 11 | + rdrxctl |= IXGBE_RDRXCTL_FCOE_WRFIX; 12 | + 13 | IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /setup/dpdk.ixgbe.unknown_eimc_bit.patch: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/net/ixgbe/base/ixgbe_type.h b/drivers/net/ixgbe/base/ixgbe_type.h 2 | index bda8558..af90462 100644 3 | --- a/drivers/net/ixgbe/base/ixgbe_type.h 4 | +++ b/drivers/net/ixgbe/base/ixgbe_type.h 5 | @@ -2007,7 +2007,7 @@ enum { 6 | #define IXGBE_FTQF_QUEUE_ENABLE 0x80000000 7 | 8 | /* Interrupt clear mask */ 9 | -#define IXGBE_IRQ_CLEAR_MASK 0xFFFFFFFF 10 | +#define IXGBE_IRQ_CLEAR_MASK 0x7FFFFFFF 11 | 12 | /* Interrupt Vector Allocation Registers */ 13 | #define IXGBE_IVAR_REG_NUM 25 14 | diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c 15 | index bead9c3..b75acf8 100644 16 | --- a/drivers/net/ixgbe/ixgbe_ethdev.c 17 | +++ b/drivers/net/ixgbe/ixgbe_ethdev.c 18 | @@ -923,7 +923,7 @@ ixgbe_disable_intr(struct ixgbe_hw *hw) 19 | if (hw->mac.type == ixgbe_mac_82598EB) { 20 | IXGBE_WRITE_REG(hw, IXGBE_EIMC, ~0); 21 | } else { 22 | - IXGBE_WRITE_REG(hw, IXGBE_EIMC, 0xFFFF0000); 23 | + IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_IRQ_CLEAR_MASK); 24 | IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(0), ~0); 25 | IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(1), ~0); 26 | } 27 | -------------------------------------------------------------------------------- /setup/dpdk.ixgbe.unknown_swfw_sync_bit.patch: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c 2 | index ff19a56..b1b39eb 100644 3 | --- a/drivers/net/ixgbe/ixgbe_ethdev.c 4 | +++ b/drivers/net/ixgbe/ixgbe_ethdev.c 5 | @@ -1107,7 +1107,7 @@ ixgbe_swfw_lock_reset(struct ixgbe_hw *hw) 6 | * lock can not be taken it is due to an improper lock of the 7 | * semaphore. 8 | */ 9 | - mask = IXGBE_GSSR_EEP_SM | IXGBE_GSSR_MAC_CSR_SM | IXGBE_GSSR_SW_MNG_SM; 10 | + mask = IXGBE_GSSR_EEP_SM | IXGBE_GSSR_MAC_CSR_SM; 11 | if (ixgbe_acquire_swfw_semaphore(hw, mask) < 0) { 12 | PMD_DRV_LOG(DEBUG, "SWFW common locks released"); 13 | } 14 | -------------------------------------------------------------------------------- /setup/dpdk.ixgbe.wrong_register_dpf_pmcf.patch: -------------------------------------------------------------------------------- 1 | diff --git a/drivers/net/ixgbe/ixgbe_rxtx.c b/drivers/net/ixgbe/ixgbe_rxtx.c 2 | index 9bc8462..1778aad 100644 3 | --- a/drivers/net/ixgbe/ixgbe_rxtx.c 4 | +++ b/drivers/net/ixgbe/ixgbe_rxtx.c 5 | @@ -4750,8 +4750,9 @@ ixgbe_dev_rx_init(struct rte_eth_dev *dev) 6 | /* Enable receipt of broadcasted frames */ 7 | fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); 8 | fctrl |= IXGBE_FCTRL_BAM; 9 | - fctrl |= IXGBE_FCTRL_DPF; 10 | - fctrl |= IXGBE_FCTRL_PMCF; 11 | + // FIXME these are bits 1 and 0 of MFLCN (0x04294) for the 82599 12 | + //fctrl |= IXGBE_FCTRL_DPF; 13 | + //fctrl |= IXGBE_FCTRL_PMCF; 14 | IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); 15 | 16 | /* 17 | -------------------------------------------------------------------------------- /setup/dpdk.memalloc.patch: -------------------------------------------------------------------------------- 1 | --- a/lib/librte_eal/linux/eal_memalloc.c 2020-05-26 15:41:39.000000000 +0000 2 | +++ b/lib/librte_eal/linux/eal_memalloc.c 2020-08-19 05:04:17.338501112 +0000 3 | @@ -565,11 +565,11 @@ 4 | */ 5 | goto unmapped; 6 | } 7 | - if (va != addr) { 8 | - RTE_LOG(DEBUG, EAL, "%s(): wrong mmap() address\n", __func__); 9 | - munmap(va, alloc_sz); 10 | - goto resized; 11 | - } 12 | + 13 | + /* According to the manual, addr parameter to mmap is a hint anyway 14 | + * so we should take the address we are given. 15 | + */ 16 | + addr = va; 17 | 18 | /* In linux, hugetlb limitations, like cgroup, are 19 | * enforced at fault time instead of mmap(), even 20 | -------------------------------------------------------------------------------- /setup/uclibc/fscanf.c: -------------------------------------------------------------------------------- 1 | int __isoc99_fscanf(FILE *stream, const char *format, ...) 2 | { 3 | va_list args; 4 | va_start(args, format); 5 | int ret = vfscanf(stream, format, args); 6 | va_end(args); 7 | return ret; 8 | } 9 | -------------------------------------------------------------------------------- /setup/uclibc/sscanf.c: -------------------------------------------------------------------------------- 1 | int __isoc99_sscanf(const char *str, const char *format, ...) 2 | { 3 | va_list args; 4 | va_start(args, format); 5 | int ret = vsscanf(str, format, args); 6 | va_end(args); 7 | return ret; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /template/Makefile: -------------------------------------------------------------------------------- 1 | # === 2 | # This Makefile describes the NF, 3 | # add the necessary files and command-line arguments here. 4 | # === 5 | 6 | # All non-generated NF files 7 | NF_FILES := main.c config.c 8 | 9 | # All structure files whose implementation should be auto-generated 10 | NF_AUTOGEN_SRCS := flow.h 11 | 12 | # Arguments used during symbolic execution, benchmarking and running 13 | NF_ARGS := --eth-dest 0,01:23:45:67:89:00 \ 14 | --eth-dest 1,01:23:45:67:89:01 15 | 16 | # Layer of the NF in the network stack, 17 | # used for benchmarking to know what levels flows are at 18 | NF_LAYER := 2 19 | 20 | # `true` if the NF needs some reverse traffic for a benchmark to be meaningful 21 | # (e.g. Maglev needs backend heartbeats) 22 | NF_BENCH_NEEDS_REVERSE_TRAFFIC := false 23 | 24 | # DO NOT CHANGE - Include parent 25 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 26 | -------------------------------------------------------------------------------- /template/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // === 4 | // This file contains your NF's configuration. 5 | // === 6 | 7 | #include 8 | 9 | struct nf_config { 10 | // MAC addresses of devices 11 | struct rte_ether_addr *device_macs; 12 | 13 | // MAC addresses of the endpoints the devices are linked to 14 | struct rte_ether_addr *endpoint_macs; 15 | }; 16 | -------------------------------------------------------------------------------- /template/dataspec.py: -------------------------------------------------------------------------------- 1 | # This file should be automatically generated by codegen script 2 | # from the dataspec.ml, but currently has to be written by hand. 3 | # it contains extracts from it translated from OCaml speak to Python. 4 | # Here you define three dictionaries: 5 | # - objConstructors maps method calls into 6 | # a dictionary with meta information about the return type of the method 7 | # if the method has a nontrivial return type. 8 | # the metainformation dictionary must define the 9 | # - name of the constructor for the type: 'constructor' 10 | # - typename: 'type' 11 | # - properties of the compound object: 'fields', an array of strings 12 | # - typeConstructors maps constructors to the names of the types they produce 13 | # - stateObjects for the NF state objects (global), which are persisted 14 | # across packet processing maps object name to the object type. 15 | # here the object type is one of predefined types: emap, vector or None 16 | 17 | # An example of an objConstructors entry: 18 | # 19 | # 'dyn_vals.get' : {'constructor' : 'DynamicValuec', 20 | # 'type' : 'DynamicValuei', 21 | # 'fields' : ['bucket_size', 'bucket_time']}}, 22 | # 23 | # This translates into: invocation of a method get on the 24 | # global object dyn_vals returns a DynamicValuei instance, 25 | # that can be constructed (or destructed) by the DynamicValuec constructor 26 | # and has at least two fields: bucket_size and bucket_time 27 | # the symbols mentioned in the type meta information are defined 28 | # in the autogenerated file dynamic_value.h.gen.h 29 | objConstructors = {} 30 | 31 | # An example of a typeConstructors entry: 32 | # 33 | # 'ip_addrc' : 'ip_addri', 34 | # 35 | # note the different suffixes. These symbols are generated by the 36 | # codegenertor script and are defined in the ip_addr.h.gen.h 37 | typeConstructors = {} 38 | 39 | # An example of a stateObjects entry: 40 | # 41 | # 'backends' : vector 42 | # 43 | # describing the backends object to be of type vector. 44 | # The backends objects, as all the other objects mentioned 45 | # in this file is declared in the dataspec.ml file. 46 | stateObjects = {} 47 | -------------------------------------------------------------------------------- /template/flow.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // === 4 | // This is a structure used in the NF state. 5 | // Upon compilation, a header and an implementation file will be generated, with 6 | // suffixes '.gen.h' and '.gen.c' respectively. They contain: 7 | // - A hash method, with suffix '_hash' (e.g. 'unsigned FlowId_hash(void*)') 8 | // - An equality method, with suffix '_eq' (e.g. 'bool FlowId_eq(void*)') 9 | // - A log method, with prefix 'log_' (e.g. 'void log_FlowId(void*)') 10 | // - Some necessary implementation details 11 | // 12 | // Due to the prototype nature of Vigor, YOU MUST MANUALLY MIRROR CHANGES TO 13 | // THIS FILE INTO dataspec.ml 14 | // === 15 | 16 | struct FlowId { 17 | uint16_t src_port; 18 | uint16_t dst_port; 19 | uint32_t src_ip; 20 | uint32_t dst_ip; 21 | uint8_t protocol; 22 | }; 23 | -------------------------------------------------------------------------------- /template/spec.py: -------------------------------------------------------------------------------- 1 | # === 2 | # This is your NF's spec 3 | # === 4 | 5 | # === 6 | # Import state if needed like this: 7 | # from state import flow_emap 8 | # === 9 | 10 | 11 | # === 12 | # You can declare constants 13 | # === 14 | SOME_VALUE = 42 15 | 16 | # === 17 | # You can also use the non-container values from the state as-is: 18 | # === 19 | assert example_value == 42 20 | 21 | # === 22 | # Variable 'a_packet_received' is true if a packet was received, false otherwise 23 | # === 24 | if not a_packet_received: 25 | # === 26 | # Return a tuple of lists: 27 | # 1. The list of devices the packet must be forwarded to; 28 | # 2. The headers the forwarded packet must have. 29 | # === 30 | return ([],[]) 31 | 32 | # === 33 | # Get input packet headers in reverse order, they are a stack. 34 | # The on_mismatch argument has the same semantics as a return; if the input packet did not have this header, it will return that value 35 | # === 36 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 37 | h2 = pop_header(ipv4, on_mismatch=([],[])) 38 | h1 = pop_header(ether, on_mismatch=([],[])) 39 | 40 | # === 41 | # You can write assertions 42 | # === 43 | assert h2.npid == 6 or h2.npid == 17 # 6/17 -> TCP/UDP 44 | 45 | # === 46 | # You can use standard Python if/else 47 | # === 48 | if 1 == 2: 49 | assert false 50 | 51 | # === 52 | # Variable 'received_on_port' contains the port on which the input packet was received 53 | # === 54 | # ... (Python's ellipsis) indicates the framework to ignore the corresponding field. 55 | # i.e. if you do not want to specify ether.saddr value, because it is set by the L2, you can 56 | # "skip" when specifying the ethernet header with 57 | # ether(h1, saddr=...) - this will enforce the same ethernet header as h1, 58 | # but with an arbitrary saddr 59 | return ([1 - received_on_port], 60 | [ether(h1, saddr=..., daddr=...), 61 | ipv4(h2, cksum=..., saddr=..., daddr=...), 62 | tcpudp(src_port=..., dst_port=...)]) 63 | -------------------------------------------------------------------------------- /validator/.gitignore: -------------------------------------------------------------------------------- 1 | *forwarding_property.tmpl 2 | *_fspec.ml 3 | _build 4 | out 5 | test-results 6 | -------------------------------------------------------------------------------- /validator/.merlin: -------------------------------------------------------------------------------- 1 | PRJ validator 2 | 3 | S . 4 | B _build 5 | 6 | PKG core -------------------------------------------------------------------------------- /validator/Makefile: -------------------------------------------------------------------------------- 1 | validator.byte: .PHONY 2 | corebuild -no-hygiene -use-menhir -lib dynlink -lib str \ 3 | validator.byte -use-ocamlfind -cflag -unsafe-string 4 | 5 | .PHONY: 6 | 7 | clean: 8 | rm -rf out 9 | rm -rf _build 10 | find -iname '*.cmo' -delete 11 | find -iname '*.cma' -delete 12 | find -iname '*.log' -delete 13 | rm -f validator.byte 14 | 15 | vig%: clean validator.byte 16 | @if [ ! -e ../$@/klee-last ]; then \ 17 | printf '!!!\n\nPlease run symbex first.\n\n!!!\n'; exit 1; \ 18 | fi 19 | @cp ../$@/fspec.ml $@_fspec.ml 20 | @corebuild -no-hygiene -lib dynlink -lib str \ 21 | common_fspec.cmo $@_fspec.cmo -cflag -unsafe-string 22 | @ocamlc -a _build/common_fspec.cmo _build/$@_fspec.cmo -o $@_fspec.cma 23 | @python3 translate-spec.py ../$@/$(or $(VIGOR_SPEC),spec.py) \ 24 | forwarding_property.tmpl ../$@ 25 | @/usr/bin/time -v ./test_all.sh ../$@/klee-last out .. $@_fspec.cma $@ 26 | 27 | # Regression test suite 28 | #test-results/%.done: regression-tests/% 29 | # ./run-test.sh $* test-results 30 | # touch $@ 31 | #check: $(patsubst regression-tests/%,test-results/%.done,$(wildcard regression-tests/*)) 32 | -------------------------------------------------------------------------------- /validator/canonicalize.verify.stdout.sh: -------------------------------------------------------------------------------- 1 | FNAME=$1 2 | STDOUT_FILE=$2 3 | 4 | sed -i "s|$FNAME||g" $STDOUT_FILE 5 | sed -i 's/([0-9]\+,[0-9-]\+)/(,)/g' $STDOUT_FILE 6 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic-negative/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=brr/000001 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic-negative/expected: -------------------------------------------------------------------------------- 1 | Invalid output. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic-negative/trace: -------------------------------------------------------------------------------- 1 | ((history ( 2 | )) 3 | (tip_calls ( 4 | ; id: 1(118) 5 | ((fun_name "map_allocate") 6 | (args ( 7 | ((aname "keq") 8 | (value (w64 49920112)) 9 | (ptr (Funptr "static_key_eq"))) 10 | ((aname "khash") 11 | (value (w64 49920560)) 12 | (ptr (Funptr "static_key_hash"))) 13 | ((aname "capacity") 14 | (value (ReadLSB w32 (w32 0) static_capacity)) 15 | (ptr Nonptr)) 16 | ((aname "map_out") 17 | (value (w64 1073752024)) 18 | (ptr (Curioptr 19 | ((before ((full ((w64 0))) 20 | (sname ()) 21 | (break_down ()))) 22 | (after ((full ((w64 0))) 23 | (sname ()) 24 | (break_down ()))))) 25 | )))) 26 | (extra_ptrs ()) 27 | (ret (((value (w32 0)) 28 | (ptr Nonptr)))) 29 | (call_context ( 30 | (Sle (w32 1) 31 | (ReadLSB w32 (w32 0) static_capacity)) 32 | (Slt (ReadLSB w32 (w32 0) static_capacity) 33 | (w32 140000)))) 34 | (ret_context ())) 35 | ; id: 2(118) 36 | ((fun_name "map_allocate") 37 | (args ( 38 | ((aname "keq") 39 | (value (w64 49920112)) 40 | (ptr (Funptr "static_key_eq"))) 41 | ((aname "khash") 42 | (value (w64 49920560)) 43 | (ptr (Funptr "static_key_hash"))) 44 | ((aname "capacity") 45 | (value (ReadLSB w32 (w32 0) static_capacity)) 46 | (ptr Nonptr)) 47 | ((aname "map_out") 48 | (value (w64 1073752024)) 49 | (ptr (Curioptr 50 | ((before ((full ((w64 0))) 51 | (sname ()) 52 | (break_down ()))) 53 | (after ((full ((w64 1073930280))) 54 | (sname ()) 55 | (break_down ()))))) 56 | )))) 57 | (extra_ptrs ()) 58 | (ret (((value (w32 -1)) 59 | (ptr Nonptr)))) 60 | (call_context ( 61 | (Sle (w32 1) 62 | (ReadLSB w32 (w32 0) static_capacity)) 63 | (Slt (ReadLSB w32 (w32 0) static_capacity) 64 | (w32 140000)))) 65 | (ret_context ())) 66 | ))) 67 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic-negative/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Assertion might not hold: false 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=brr/000001 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic/trace: -------------------------------------------------------------------------------- 1 | ((history ( 2 | )) 3 | (tip_calls ( 4 | ; id: 1(118) 5 | ((fun_name "map_allocate") 6 | (args ( 7 | ((aname "keq") 8 | (value (w64 49920112)) 9 | (ptr (Funptr "static_key_eq"))) 10 | ((aname "khash") 11 | (value (w64 49920560)) 12 | (ptr (Funptr "static_key_hash"))) 13 | ((aname "capacity") 14 | (value (ReadLSB w32 (w32 0) static_capacity)) 15 | (ptr Nonptr)) 16 | ((aname "map_out") 17 | (value (w64 1073752024)) 18 | (ptr (Curioptr 19 | ((before ((full ((w64 0))) 20 | (sname ()) 21 | (break_down ()))) 22 | (after ((full ((w64 0))) 23 | (sname ()) 24 | (break_down ()))))) 25 | )))) 26 | (extra_ptrs ()) 27 | (ret (((value (w32 0)) 28 | (ptr Nonptr)))) 29 | (call_context ( 30 | (Sle (w32 1) 31 | (ReadLSB w32 (w32 0) static_capacity)) 32 | (Slt (ReadLSB w32 (w32 0) static_capacity) 33 | (w32 140000)))) 34 | (ret_context ())) 35 | ; id: 2(118) 36 | ((fun_name "map_allocate") 37 | (args ( 38 | ((aname "keq") 39 | (value (w64 49920112)) 40 | (ptr (Funptr "static_key_eq"))) 41 | ((aname "khash") 42 | (value (w64 49920560)) 43 | (ptr (Funptr "static_key_hash"))) 44 | ((aname "capacity") 45 | (value (ReadLSB w32 (w32 0) static_capacity)) 46 | (ptr Nonptr)) 47 | ((aname "map_out") 48 | (value (w64 1073752024)) 49 | (ptr (Curioptr 50 | ((before ((full ((w64 0))) 51 | (sname ()) 52 | (break_down ()))) 53 | (after ((full ((w64 1073930280))) 54 | (sname ()) 55 | (break_down ()))))) 56 | )))) 57 | (extra_ptrs ()) 58 | (ret (((value (w32 1)) 59 | (ptr Nonptr)))) 60 | (call_context ( 61 | (Sle (w32 1) 62 | (ReadLSB w32 (w32 0) static_capacity)) 63 | (Slt (ReadLSB w32 (w32 0) static_capacity) 64 | (w32 140000)))) 65 | (ret_context ())) 66 | ))) 67 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/basic/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/better-type-guessing/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=nat_fspec.cmo 2 | ORIG_PREFIX=out/000123 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/better-type-guessing/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/better-type-guessing/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/bridge-pointer-coherence-between-map-and-vector/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=brr/000026 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/bridge-pointer-coherence-between-map-and-vector/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/bridge-pointer-coherence-between-map-and-vector/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/empty-map-vec-dchain-coherent-call/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=brr/000006 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/empty-map-vec-dchain-coherent-call/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/empty-map-vec-dchain-coherent-call/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/handling-arrays-properly/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=out/000186 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/handling-arrays-properly/expected: -------------------------------------------------------------------------------- 1 | Inconsistent. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/handling-arrays-properly/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/mvc-coherent-key-abscent/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=bridge_fspec.cmo 2 | ORIG_PREFIX=brr/000020 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/mvc-coherent-key-abscent/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/mvc-coherent-key-abscent/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/support-sexp-or/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=balancer_fspec.cmo 2 | ORIG_PREFIX=out/000047 3 | REPLACED_WITH_PREFIX=out/000020 4 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/support-sexp-or/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/support-sexp-or/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/uint32-to-16-cast-needs-bit-and/config: -------------------------------------------------------------------------------- 1 | FSPEC_PLUGIN=nat_fspec.cmo 2 | ORIG_PREFIX=out/000045 3 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/uint32-to-16-cast-needs-bit-and/expected: -------------------------------------------------------------------------------- 1 | Valid. 2 | -------------------------------------------------------------------------------- /validator/disabled-regression-tests/uint32-to-16-cast-needs-bit-and/verify.stdout.expected: -------------------------------------------------------------------------------- 1 | 2 | (,): Breakpoint reached. 3 | -------------------------------------------------------------------------------- /validator/import.mli: -------------------------------------------------------------------------------- 1 | open Core 2 | open Fspec_api 3 | open Ir 4 | 5 | val build_ir: fun_spec String.Map.t -> 6 | string -> 7 | string -> 8 | string -> 9 | string -> 10 | string -> 11 | string -> 12 | ir 13 | -------------------------------------------------------------------------------- /validator/render.mli: -------------------------------------------------------------------------------- 1 | open Ir 2 | 3 | val render_ir: ir -> string -> render_assertions:bool -> unit 4 | -------------------------------------------------------------------------------- /validator/trace_prefix.ml: -------------------------------------------------------------------------------- 1 | 2 | open Core.Sexp 3 | open Sexplib.Conv 4 | 5 | type expr = Core.Sexp.t [@@deriving sexp] 6 | 7 | type field = {fname: string; value: struct_val; addr: int64} 8 | and struct_val = {sname: string option; 9 | full: expr option; 10 | break_down: field list;} [@@deriving sexp] 11 | 12 | type ptee = {before: struct_val; after: struct_val;} [@@deriving sexp] 13 | 14 | type ex_ptee = Opening of struct_val 15 | | Closing of struct_val 16 | | Changing of (struct_val*struct_val) [@@deriving sexp] 17 | 18 | type pointer = 19 | | Nonptr 20 | | Funptr of string 21 | | Apathptr 22 | | Curioptr of ptee [@@deriving sexp] 23 | 24 | type arg = {aname: string; value: expr; ptr: pointer;} [@@deriving sexp] 25 | 26 | type extra_ptr = {pname: string; value: int64; ptee: ex_ptee;} [@@deriving sexp] 27 | 28 | type ret = {value: expr; ptr: pointer;} [@@deriving sexp] 29 | 30 | type call_node = {fun_name: string; args: arg list; ret: ret option; 31 | extra_ptrs: extra_ptr list; 32 | call_context: expr list; ret_context: expr list; 33 | id: int [@default 0];} [@@deriving sexp] 34 | 35 | type trace_prefix = {history: call_node list; 36 | tip_calls: call_node list;} [@@deriving sexp] 37 | -------------------------------------------------------------------------------- /vigbridge/Makefile: -------------------------------------------------------------------------------- 1 | NF_FILES := bridge_main.c bridge_config.c 2 | 3 | NF_AUTOGEN_SRCS := dyn_value.h stat_key.h 4 | 5 | NF_ARGS := --expire $(or $(EXPIRATION_TIME),10) --capacity $(or $(CAPACITY),65536) 6 | 7 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 8 | -------------------------------------------------------------------------------- /vigbridge/bridge_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "libvig/verified/vigor-time.h" 5 | 6 | #define CONFIG_FNAME_LEN 512 7 | 8 | struct nf_config { 9 | // Expiration time of flows, in microseconds 10 | uint32_t expiration_time; 11 | 12 | // Size of the dynamic filtering table 13 | uint32_t dyn_capacity; 14 | 15 | // The static configuration file name 16 | char static_config_fname[CONFIG_FNAME_LEN]; 17 | }; 18 | -------------------------------------------------------------------------------- /vigbridge/dataspec.ml: -------------------------------------------------------------------------------- 1 | open Data_spec 2 | open Core 3 | open Ir 4 | 5 | let containers = ["dyn_map", Map ("rte_ether_addr", "capacity", ""); 6 | "dyn_keys", Vector ("rte_ether_addr", "capacity", ""); 7 | "dyn_vals", Vector ("DynamicValue", "capacity", "dyn_val_condition"); 8 | "st_map", Map ("StaticKey", "stat_capacity", "stat_map_condition"); 9 | "st_vec", Vector ("StaticKey", "stat_capacity", ""); 10 | "dyn_heap", DChain "capacity"; 11 | "capacity", UInt32; 12 | "stat_capacity", UInt32; 13 | "dev_count", UInt32; 14 | "dyn_emap", EMap ("rte_ether_addr", "dyn_map", "dyn_keys", "dyn_heap")] 15 | 16 | let constraints = ["dyn_val_condition", ( "DynamicValue", 17 | [Bop (Le, {t=Unknown;v=Int 0}, {t=Unknown;v=Id "device"}); 18 | Bop (Lt, {t=Unknown;v=Id "device"}, {t=Unknown;v=Int 2}); 19 | ]); 20 | "stat_map_condition", ( "StaticKey", 21 | [Bop (Le, {t=Unknown;v=Int 0}, {t=Unknown;v=Id "index"}); 22 | Bop (Lt, {t=Unknown;v=Id "index"}, {t=Unknown;v=Int 2}); 23 | ])] 24 | 25 | let gen_custom_includes = ref [] 26 | let gen_records = ref [] 27 | -------------------------------------------------------------------------------- /vigbridge/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {'dyn_vals.get' : {'constructor' : 'DynamicValuec', 2 | 'type' : 'DynamicValuei', 3 | 'fields' : ['output_port']}} 4 | typeConstructors = {'StaticKeyc' : 'StaticKeyi'} 5 | stateObjects = {'dyn_emap' : emap, 6 | 'dyn_vals' : vector, 7 | 'stat_emap' : emap} 8 | customPreamble = """ 9 | dchain stat_chain; //dummy 10 | emap stat_emap = emap(initial_st_map, initial_st_vec, stat_chain); 11 | emap final_stat_emap = emap(initial_st_map, initial_st_vec, stat_chain); 12 | """ 13 | -------------------------------------------------------------------------------- /vigbridge/dyn_value.h: -------------------------------------------------------------------------------- 1 | #ifndef _DYN_VALUE_H_INCLUDED_ 2 | #define _DYN_VALUE_H_INCLUDED_ 3 | 4 | #include 5 | 6 | struct DynamicValue { 7 | uint16_t device; 8 | }; 9 | 10 | #endif //_DYN_VALUE_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /vigbridge/fspec.ml: -------------------------------------------------------------------------------- 1 | open Str 2 | open Core 3 | open Fspec_api 4 | open Ir 5 | open Common_fspec 6 | 7 | let static_key_struct = Ir.Str ( "StaticKey", ["addr", rte_ether_addr_struct; 8 | "device", Uint16] ) 9 | let dynamic_value_struct = Ir.Str ( "DynamicValue", ["device", Uint16] ) 10 | 11 | (* TODO: make external_ip symbolic *) 12 | module Iface : Fspec_api.Spec = 13 | struct 14 | (* FIXME: borrowed from ../nf/vigbridge/bridge_data_spec.ml*) 15 | let containers = ["dyn_map", Map ("rte_ether_addr", "capacity", ""); 16 | "dyn_keys", Vector ("rte_ether_addr", "capacity", ""); 17 | "dyn_vals", Vector ("DynamicValue", "capacity", "dyn_val_condition"); 18 | "st_map", Map ("StaticKey", "stat_capacity", "stat_map_condition"); 19 | "st_vec", Vector ("StaticKey", "stat_capacity", ""); 20 | "dyn_heap", DChain "capacity"; 21 | "capacity", UInt32; 22 | "stat_capacity", UInt32; 23 | "dev_count", UInt32; 24 | "dyn_emap", EMap ("rte_ether_addr", "dyn_map", "dyn_keys", "dyn_heap")] 25 | 26 | let records = String.Map.of_alist_exn 27 | ["rte_ether_addr", rte_ether_addr_struct; 28 | "DynamicValue", dynamic_value_struct; 29 | "StaticKey", static_key_struct] 30 | end 31 | 32 | (* Register the module *) 33 | let () = 34 | Fspec_api.spec := Some (module Iface) ; 35 | 36 | -------------------------------------------------------------------------------- /vigbridge/optimize.patch: -------------------------------------------------------------------------------- 1 | diff --git b/bridge_main.c a/bridge_main.c 2 | index e561b061..acaf715a 100644 3 | --- b/bridge_main.c 4 | +++ a/bridge_main.c 5 | @@ -32,20 +32,10 @@ struct nf_config config; 6 | struct State *mac_tables; 7 | 8 | int bridge_expire_entries(vigor_time_t time) { 9 | - if (time < config.expiration_time) 10 | - return 0; 11 | - // This is convoluted - we want to make sure the sanitization doesn't 12 | - // extend our vigor_time_t value in 128 bits, which would confuse the 13 | - // validator. So we "prove" by hand that it's OK... We know time >= 0 since 14 | - // time >= config->expiration_time 15 | - assert(sizeof(vigor_time_t) <= sizeof(int64_t)); 16 | - uint64_t time_u = (uint64_t)time; // OK since assert above passed and time > 0 17 | - uint64_t min_time_u = 18 | - time_u - 19 | - config.expiration_time * 1000; // OK because time >= expiration_time >= 0 20 | - assert(sizeof(int64_t) <= sizeof(vigor_time_t)); 21 | - vigor_time_t last_time = 22 | - (vigor_time_t)min_time_u; // OK since the assert above passed 23 | + assert(time >= 0); // we don't support the past 24 | + assert(sizeof(vigor_time_t) <= sizeof(uint64_t)); 25 | + uint64_t time_u = (uint64_t)time; // OK because of the two asserts 26 | + vigor_time_t last_time = time_u - config.expiration_time * 1000; // us to ns 27 | return expire_items_single_map(mac_tables->dyn_heap, mac_tables->dyn_keys, 28 | mac_tables->dyn_map, last_time); 29 | } 30 | -------------------------------------------------------------------------------- /vigbridge/paygo-broadcast.py: -------------------------------------------------------------------------------- 1 | import table_updates 2 | h = pop_header(ether, on_mismatch=([],[])) 3 | static_key = StaticKeyc(h.daddr, received_on_port) 4 | if not stat_emap.has(static_key) and not dyn_emap.has(h.daddr): 5 | return ([1-received_on_port], [ether(h)]) # broadcast, TODO: generalize 6 | -------------------------------------------------------------------------------- /vigbridge/paygo-drop_or_forward.py: -------------------------------------------------------------------------------- 1 | import table_updates 2 | h = pop_header(ether, on_mismatch=([],[])) 3 | static_key = StaticKeyc(h.daddr, received_on_port) 4 | if stat_emap.has(static_key): 5 | if (stat_emap.get(static_key) == -2 or stat_emap.get(static_key) == received_on_port): 6 | return ([],[]) 7 | elif dyn_emap.has(h.daddr) and dyn_vals.get(dyn_emap.get(h.daddr)) == DynamicValuec(received_on_port): 8 | return ([],[]) 9 | else: 10 | return ([...],[ether(h)]) 11 | -------------------------------------------------------------------------------- /vigbridge/paygo-learn.py: -------------------------------------------------------------------------------- 1 | from state import dyn_emap 2 | EXP_TIME = 10 * 1000 3 | h = pop_header(ether, on_mismatch=([],[])) 4 | dyn_emap.expire_all(now - EXP_TIME) 5 | if dyn_emap.has(h.saddr): 6 | pass 7 | elif not dyn_emap.full(): 8 | idx = the_index_allocated 9 | dyn_emap.add(h.saddr, idx, now) 10 | dyn_vals.set(idx, DynamicValuec(received_on_port)) 11 | -------------------------------------------------------------------------------- /vigbridge/paygo-no_loop.py: -------------------------------------------------------------------------------- 1 | assert not mem(received_on_port, sent_on_ports) 2 | -------------------------------------------------------------------------------- /vigbridge/paygo-refresh.py: -------------------------------------------------------------------------------- 1 | from state import dyn_emap 2 | EXP_TIME = 10 * 1000 3 | h = pop_header(ether, on_mismatch=([],[])) 4 | dyn_emap.expire_all(now - EXP_TIME) 5 | if dyn_emap.has(h.saddr): 6 | dyn_emap.refresh_idx(dyn_emap.get(h.saddr), now) 7 | else: 8 | pass 9 | -------------------------------------------------------------------------------- /vigbridge/paygo-static_drop.py: -------------------------------------------------------------------------------- 1 | from state import stat_emap 2 | h = pop_header(ether, on_mismatch=([],[])) 3 | static_key = StaticKeyc(h.daddr, received_on_port) 4 | if stat_emap.has(static_key) and (stat_emap.get(static_key) == -2): 5 | return ([],[]) 6 | -------------------------------------------------------------------------------- /vigbridge/paygo-table_updates.py: -------------------------------------------------------------------------------- 1 | from state import dyn_emap, stat_emap, dyn_vals 2 | EXP_TIME = 10 * 1000 3 | h = pop_header(ether, on_mismatch=([],[])) 4 | dyn_emap.expire_all(now - EXP_TIME) 5 | if dyn_emap.has(h.saddr): 6 | dyn_emap.refresh_idx(dyn_emap.get(h.saddr), now) 7 | else: 8 | if not dyn_emap.full(): 9 | idx = the_index_allocated 10 | dyn_emap.add(h.saddr, idx, now) 11 | dyn_vals.set(idx, DynamicValuec(received_on_port)) 12 | -------------------------------------------------------------------------------- /vigbridge/spec.py: -------------------------------------------------------------------------------- 1 | from state import dyn_emap, stat_emap, dyn_vals 2 | EXP_TIME = 10 * 1000 3 | 4 | h = pop_header(ether, on_mismatch=([],[])) 5 | 6 | dyn_emap.expire_all(now - EXP_TIME) 7 | 8 | if dyn_emap.has(h.saddr): 9 | dyn_emap.refresh_idx(dyn_emap.get(h.saddr), now) 10 | else: 11 | if not dyn_emap.full(): 12 | idx = the_index_allocated 13 | dyn_emap.add(h.saddr, idx, now) 14 | dyn_vals.set(idx, DynamicValuec(received_on_port)) 15 | 16 | static_key = StaticKeyc(h.daddr, received_on_port) 17 | if stat_emap.has(static_key): 18 | output_port = stat_emap.get(static_key) 19 | if output_port == -2 or output_port == received_on_port: 20 | return ([],[]) 21 | else: 22 | return ([output_port], [ether(h)]) 23 | else: 24 | if dyn_emap.has(h.daddr): 25 | idx = dyn_emap.get(h.daddr) 26 | forward_to = dyn_vals.get(idx) 27 | if (forward_to.output_port == received_on_port): 28 | return ([],[]) 29 | else: 30 | return ([forward_to.output_port], [ether(h)]) 31 | else: 32 | return ([1-received_on_port], [ether(h)]) # broadcast, TODO: generalize 33 | 34 | -------------------------------------------------------------------------------- /vigbridge/stat_key.h: -------------------------------------------------------------------------------- 1 | #ifndef _STAT_KEY_H_INCLUDED_ 2 | #define _STAT_KEY_H_INCLUDED_ 3 | 4 | #include 5 | #include 6 | 7 | struct StaticKey { 8 | struct rte_ether_addr addr; 9 | uint16_t device; 10 | }; 11 | 12 | #endif //_STAT_KEY_H_INCLUDED_ 13 | -------------------------------------------------------------------------------- /vigbridge/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd) 6 | 7 | function cleanup { 8 | sudo killall bridge 2>/dev/null || true 9 | sudo killall iperf 2>/dev/null || true 10 | sudo ip netns delete lan 2>/dev/null || true 11 | sudo ip netns delete wan 2>/dev/null || true 12 | } 13 | trap cleanup EXIT 14 | 15 | 16 | function test_bridge { 17 | RATE=$1 18 | BURST=$2 19 | 20 | sudo ./build/app/bridge \ 21 | --vdev "net_tap0,iface=test_wan" \ 22 | --vdev "net_tap1,iface=test_lan" \ 23 | --no-shconf -- \ 24 | --expire 10 --capacity 100 --config no-file.cfg \ 25 | #>/dev/null 2>/dev/null & 26 | NF_PID=$! 27 | 28 | while [ ! -f /sys/class/net/test_lan/tun_flags -o \ 29 | ! -f /sys/class/net/test_lan/tun_flags ]; do 30 | echo "Waiting for NF to launch..."; 31 | sleep 1; 32 | done 33 | sleep 2 34 | 35 | sudo ip netns add lan 36 | sudo ip link set test_lan netns lan 37 | sudo ip netns exec lan ifconfig test_lan up 10.0.0.1 38 | LAN_MAC=$(sudo ip netns exec lan ifconfig test_lan | head -n 1 | \ 39 | awk '{ print $5 }') 40 | 41 | sudo ip netns add wan 42 | sudo ip link set test_wan netns wan 43 | sudo ip netns exec wan ifconfig test_wan up 10.0.0.2 44 | WAN_MAC=$(sudo ip netns exec wan ifconfig test_wan | head -n 1 | \ 45 | awk '{ print $5 }') 46 | 47 | sudo ip netns exec lan arp -i test_lan -s 10.0.0.2 $WAN_MAC 48 | sudo ip netns exec wan arp -i test_wan -s 10.0.0.1 $LAN_MAC 49 | 50 | sudo ip netns exec lan iperf -us -i 1 & 51 | SERVER_PID=$! 52 | 53 | sudo ip netns exec wan iperf -uc 10.0.0.1 -t 10 >/dev/null 54 | 55 | sudo killall iperf 56 | wait $SERVER_PID 2>/dev/null || true 57 | 58 | sudo killall bridge 59 | wait $NF_PID 2>/dev/null || true 60 | 61 | sudo ip netns delete lan 62 | sudo ip netns delete wan 63 | } 64 | 65 | 66 | make clean 67 | make ADDITIONAL_FLAGS="-DSTOP_ON_RX_0 -g" -j$(nproc) 68 | 69 | test_bridge 12500 500000 70 | 71 | echo "Done." 72 | -------------------------------------------------------------------------------- /vigbridge/testbed/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.box = "bento/ubuntu-14.04" 6 | 7 | config.vm.define :server do |srv| 8 | srv.vm.hostname = "ext-serv" 9 | srv.vm.network "private_network", ip: "192.168.33.10", :mac => "080027538b38" 10 | srv.vm.provision "shell", path: "server.sh" 11 | config.ssh.forward_x11 = true 12 | end 13 | 14 | config.vm.define :bridge do |bridge| 15 | bridge.vm.hostname = "bridge" 16 | bridge.vm.network "private_network", ip: "192.168.33.2", :mac => "080027004471" 17 | bridge.vm.network "private_network", ip: "192.168.33.2", :mac => "080027004472" 18 | bridge.vm.synced_folder "./src", "/nf" 19 | bridge.vm.provision "shell", path: "bridge.sh" 20 | config.ssh.forward_x11 = true 21 | bridge.vm.provider "virtualbox" do |v| 22 | v.memory = 2048 23 | end 24 | end 25 | 26 | config.vm.define :client do |cl| 27 | cl.vm.hostname = "int-clnt" 28 | cl.vm.network "private_network", ip: "192.168.33.5", :mac => "080027c11347" 29 | cl.vm.provision "shell", path: "client.sh" 30 | config.ssh.forward_x11 = true 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /vigbridge/testbed/bridge.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get update 2 | sudo apt-get install -y tcpdump git wget build-essential libpcap-dev linux-headers-3.13.0-112-generic libglib2.0-dev daemon 3 | 4 | wget http://dpdk.org/browse/dpdk/snapshot/dpdk-16.04.tar.gz -O dpdk.tar.gz 5 | tar xf dpdk.tar.gz 6 | mv dpdk-* dpdk 7 | rm dpdk.tar.gz 8 | 9 | cd dpdk 10 | sed -ri 's,(PMD_PCAP=).*,\1y,' config/common_linuxapp 11 | make config T=x86_64-native-linuxapp-gcc 12 | make install -j2 T=x86_64-native-linuxapp-gcc DESTDIR=. 13 | 14 | echo 'export RTE_SDK=/home/vagrant/dpdk' >> /home/vagrant/.bashrc 15 | echo 'export RTE_TARGET=x86_64-native-linuxapp-gcc' >> /home/vagrant/.bashrc 16 | 17 | -------------------------------------------------------------------------------- /vigbridge/testbed/client.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get update 2 | sudo apt-get install -y tcpdump hping3 python-scapy wget 3 | sudo arp -s 192.168.33.10 08:00:27:53:8b:38 4 | #sudo ip route add 192.168.33.10 dev eth1 5 | 6 | #sudo ip route add 192.168.33.0/24 via 192.168.40.2 dev eth1 7 | -------------------------------------------------------------------------------- /vigbridge/testbed/run-nat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | EXPTIME=$1 4 | MAX_FLOWS=$2 5 | 6 | if [ -z $EXPTIME ]; then 7 | EXPTIME=10 8 | fi 9 | 10 | if [ -z $MAX_FLOWS ]; then 11 | MAX_FLOWS=1024 12 | fi 13 | 14 | sudo /nat/build/nat -c 0x01 -n 2 -- -p 0x3 --wan 0 --expire $EXPTIME --max-flows $MAX_FLOWS --starting-port 1025 --extip 192.168.33.2 --eth-dest 0,08:00:27:53:8b:38 --eth-dest 1,08:00:27:c1:13:47 15 | 16 | -------------------------------------------------------------------------------- /vigbridge/testbed/server.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get update && sudo apt-get install -y tcpdump hping3 apache2 2 | sudo arp -s 192.168.33.5 08:00:27:c1:13:47 3 | 4 | -------------------------------------------------------------------------------- /vigbridge/testbed/src: -------------------------------------------------------------------------------- 1 | ../.. -------------------------------------------------------------------------------- /vigbridge/testbed/start-nat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start the Vigor NAT in the background (as a daemon). 3 | 4 | . ./hard/config.sh 5 | 6 | . ./redeploy-nat.sh 7 | daemon ./run-nat.sh $PWD 8 | -------------------------------------------------------------------------------- /vigbridge/testbed/test-nat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | vagrant up 4 | vagrant ssh -c 'wget -O /dev/null -T 1 -t 1 192.168.33.10' client 5 | if [ $? -eq 0 ]; then 6 | echo "NAT works!" 7 | else 8 | echo "Something wrong" 9 | fi 10 | -------------------------------------------------------------------------------- /vigfw/.merlin: -------------------------------------------------------------------------------- 1 | S . -------------------------------------------------------------------------------- /vigfw/Makefile: -------------------------------------------------------------------------------- 1 | NF_FILES := fw_main.c fw_config.c fw_flowmanager.c 2 | 3 | NF_AUTOGEN_SRCS := flow.h 4 | 5 | NF_ARGS := --wan 1 \ 6 | --expire $(or $(EXPIRATION_TIME),10) \ 7 | --max-flows $(or $(CAPACITY),65536) \ 8 | --eth-dest 0,$(or $(TESTER_MAC_EXTERNAL),01:23:45:67:89:00) \ 9 | --eth-dest 1,$(or $(TESTER_MAC_INTERNAL),01:23:45:67:89:01) 10 | 11 | NF_LAYER := 4 12 | 13 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 14 | -------------------------------------------------------------------------------- /vigfw/dataspec.ml: -------------------------------------------------------------------------------- 1 | open Data_spec 2 | open Core 3 | open Ir 4 | 5 | let containers = ["fm", Map ("FlowId", "max_flows", ""); 6 | "fv", Vector ("FlowId", "max_flows", ""); 7 | "int_devices", Vector ("uint32_t", "max_flows", "int_dev_bounds"); 8 | "heap", DChain "max_flows"; 9 | "max_flows", Int; 10 | "fw_device", UInt32; 11 | "flow_emap", EMap ("FlowId", "fm", "fv", "heap")] 12 | 13 | let constraints = ["int_dev_bounds", ( "uint32_t", 14 | [Bop (Lt, {t=Unknown;v=Id "v"}, {t=Unknown;v=Int 2}); 15 | Not {v=Bop (Eq, {t=Unknown;v=Id "v"}, {t=Unknown;v=Int 1}); 16 | t=Unknown}; 17 | ])] 18 | 19 | let gen_custom_includes = ref [] 20 | let gen_records = ref [] 21 | -------------------------------------------------------------------------------- /vigfw/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {'flow_emap.get_key' : {'constructor' : 'FlowIdc', 2 | 'type' : 'FlowIdi', 3 | 'fields' : ['sp', 'dp', 'sip', 4 | 'dip', 'prot']}} 5 | typeConstructors = {'FlowIdc' : 'FlowIdi', 6 | 'emap' : 'emap'} 7 | stateObjects = {'flow_emap' : emap, 8 | 'int_devices' : vector} 9 | -------------------------------------------------------------------------------- /vigfw/flow.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct FlowId { 4 | uint16_t src_port; 5 | uint16_t dst_port; 6 | uint32_t src_ip; 7 | uint32_t dst_ip; 8 | uint8_t protocol; 9 | }; 10 | -------------------------------------------------------------------------------- /vigfw/fspec.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Str 3 | open Fspec_api 4 | open Ir 5 | open Common_fspec 6 | 7 | let flow_id_struct = Ir.Str ( "FlowId", ["src_port", Uint16; 8 | "dst_port", Uint16; 9 | "src_ip", Uint32; 10 | "dst_ip", Uint32; 11 | "protocol", Uint8;] ) 12 | 13 | module Iface : Fspec_api.Spec = 14 | struct 15 | (* FIXME: borrowed from ../nf/vigfw/fw_data_spec.ml *) 16 | let containers = ["fm", Map ("FlowId", "max_flows", ""); 17 | "fv", Vector ("FlowId", "max_flows", ""); 18 | "int_devices", Vector ("uint32_t", "max_flows", "int_dev_bounds"); 19 | "heap", DChain "max_flows"; 20 | "max_flows", Int; 21 | "fw_device", UInt32; 22 | "flow_emap", EMap ("FlowId", "fm", "fv", "heap")] 23 | 24 | let records = String.Map.of_alist_exn 25 | ["FlowId", flow_id_struct; 26 | "uint32_t", Uint32;] 27 | end 28 | 29 | (* Register the module *) 30 | let () = 31 | Fspec_api.spec := Some (module Iface) ; 32 | 33 | -------------------------------------------------------------------------------- /vigfw/fw_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "nf.h" 8 | 9 | struct nf_config { 10 | // WAN device, i.e. external 11 | uint16_t wan_device; 12 | 13 | // MAC addresses of devices 14 | struct rte_ether_addr *device_macs; 15 | 16 | // MAC addresses of the endpoints the devices are linked to 17 | struct rte_ether_addr *endpoint_macs; 18 | 19 | // Expiration time of flows, in microseconds 20 | uint32_t expiration_time; 21 | 22 | // Size of the flow table 23 | uint32_t max_flows; 24 | }; 25 | -------------------------------------------------------------------------------- /vigfw/fw_flowmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLOWMANAGER_H_INCLUDED_ 2 | #define _FLOWMANAGER_H_INCLUDED_ 3 | 4 | #include "flow.h.gen.h" 5 | #include "libvig/verified/vigor-time.h" 6 | 7 | #include 8 | #include 9 | 10 | struct FlowManager; 11 | 12 | struct FlowManager *flow_manager_allocate(uint16_t fw_device, 13 | vigor_time_t expiration_time, 14 | uint64_t max_flows); 15 | 16 | void flow_manager_allocate_or_refresh_flow(struct FlowManager *manager, 17 | struct FlowId *id, 18 | uint32_t internal_device, 19 | vigor_time_t time); 20 | void flow_manager_expire(struct FlowManager *manager, vigor_time_t time); 21 | bool flow_manager_get_refresh_flow(struct FlowManager *manager, 22 | struct FlowId *id, vigor_time_t time, 23 | uint32_t *internal_device); 24 | 25 | #endif //_FLOWMANAGER_H_INCLUDED_ 26 | -------------------------------------------------------------------------------- /vigfw/optimize.patch: -------------------------------------------------------------------------------- 1 | diff --git b/fw_flowmanager.c a/fw_flowmanager.c 2 | index ebc31995..97486898 100644 3 | --- b/fw_flowmanager.c 4 | +++ a/fw_flowmanager.c 5 | @@ -68,21 +68,12 @@ void flow_manager_allocate_or_refresh_flow(struct FlowManager *manager, 6 | } 7 | 8 | void flow_manager_expire(struct FlowManager *manager, vigor_time_t time) { 9 | - if (time < manager->expiration_time) 10 | - return; // nothing can expire yet 11 | - // This is convoluted - we want to make sure the sanitization doesn't 12 | - // extend our vigor_time_t value in 128 bits, which would confuse the 13 | - // validator. So we "prove" by hand that it's OK... 14 | - if (time < 0) 15 | - return; // we don't support the past 16 | - assert(sizeof(vigor_time_t) <= sizeof(int64_t)); 17 | - uint64_t time_u = (uint64_t)time; // OK since assert above passed and time > 0 18 | - uint64_t last_time_u = 19 | - time_u - manager->expiration_time * 20 | - 1000; // OK because time >= expiration_time >= 0 21 | - assert(sizeof(int64_t) <= sizeof(vigor_time_t)); 22 | - vigor_time_t last_time = 23 | - (vigor_time_t)last_time_u; // OK since the assert above passed 24 | + assert(time >= 0); // we don't support the past 25 | + assert(sizeof(vigor_time_t) <= sizeof(uint64_t)); 26 | + uint64_t time_u = (uint64_t)time; // OK because of the two asserts 27 | + vigor_time_t last_time = time_u - manager->expiration_time * 1000; // us to ns 28 | + expire_items_single_map(manager->state->heap, manager->state->fv, 29 | + manager->state->fm, last_time); 30 | } 31 | 32 | bool flow_manager_get_refresh_flow(struct FlowManager *manager, 33 | -------------------------------------------------------------------------------- /vigfw/paygo-block_unsolicited.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | internal_flow = FlowIdc(h3.dst_port, h3.src_port, h2.daddr, h2.saddr, h2.npid) 13 | if received_on_port == EXT_DEVICE and not flow_emap.has(internal_flow): 14 | return ([],[]) 15 | else: 16 | pass 17 | -------------------------------------------------------------------------------- /vigfw/paygo-flowtable_refresh.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, int_devices 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | if received_on_port == EXT_DEVICE: 13 | internal_flow = FlowIdc(h3.dst_port, h3.src_port, h2.daddr, h2.saddr, h2.npid) 14 | if flow_emap.has(internal_flow): 15 | flow_emap.refresh_idx(flow_emap.get(internal_flow), now) 16 | else: 17 | pass 18 | else: 19 | internal_flow = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, h2.npid) 20 | if flow_emap.has(internal_flow): 21 | flow_emap.refresh_idx(flow_emap.get(internal_flow), now) 22 | else: 23 | pass 24 | -------------------------------------------------------------------------------- /vigfw/paygo-outbound_forward.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, int_devices 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | internal_flow = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, h2.npid) 13 | if received_on_port != EXT_DEVICE: 14 | return ([EXT_DEVICE],[ether(h1, saddr=..., daddr=...), 15 | ipv4(h2, cksum=...), 16 | tcpudp(h3)]) 17 | pass # Ignore the state change 18 | else: 19 | pass 20 | -------------------------------------------------------------------------------- /vigfw/paygo-outbound_open_flow.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, int_devices 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | internal_flow = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, h2.npid) 13 | if (received_on_port != EXT_DEVICE and 14 | not flow_emap.has(internal_flow) and 15 | not flow_emap.full()) : 16 | fl_id = the_index_allocated 17 | flow_emap.add(internal_flow, fl_id, now) 18 | vector_set(int_devices, fl_id, received_on_port) 19 | else: 20 | pass 21 | -------------------------------------------------------------------------------- /vigfw/paygo-reply_forward.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, int_devices 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | internal_flow = FlowIdc(h3.dst_port, h3.src_port, h2.daddr, h2.saddr, h2.npid) 13 | if received_on_port == EXT_DEVICE and flow_emap.has(internal_flow): 14 | fl_id = flow_emap.get(internal_flow) 15 | flow_emap.refresh_idx(fl_id, now) 16 | out_port = vector_get(int_devices, fl_id) 17 | return ([out_port],[ether(h1, saddr=..., daddr=...), 18 | ipv4(h2, cksum=...), 19 | tcpudp(h3)]) 20 | else: 21 | pass 22 | -------------------------------------------------------------------------------- /vigfw/spec.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, int_devices 2 | EXP_TIME = 10 * 1000 3 | EXT_DEVICE = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | if received_on_port == EXT_DEVICE: 13 | internal_flow = FlowIdc(h3.dst_port, h3.src_port, h2.daddr, h2.saddr, h2.npid) 14 | if flow_emap.has(internal_flow): 15 | fl_id = flow_emap.get(internal_flow) 16 | flow_emap.refresh_idx(fl_id, now) 17 | out_port = vector_get(int_devices, fl_id) 18 | return ([out_port],[ether(h1, saddr=..., daddr=...), 19 | ipv4(h2, cksum=...), 20 | tcpudp(h3)]) 21 | else: 22 | return ([],[]) 23 | else: 24 | internal_flow = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, h2.npid) 25 | if flow_emap.has(internal_flow): 26 | fl_id = flow_emap.get(internal_flow) 27 | flow_emap.refresh_idx(fl_id, now) 28 | else: 29 | if not flow_emap.full(): 30 | fl_id = the_index_allocated 31 | flow_emap.add(internal_flow, fl_id, now) 32 | vector_set(int_devices, fl_id, received_on_port) 33 | return ([EXT_DEVICE],[ether(h1, saddr=..., daddr=...), 34 | ipv4(h2, cksum=...), 35 | tcpudp(h3)]) 36 | -------------------------------------------------------------------------------- /viglb/Makefile: -------------------------------------------------------------------------------- 1 | # Need 3 devices otherwise the concept of a load-balancer doesn't make sense 2 | NF_DEVICES := 3 3 | 4 | NF_FILES := lb_main.c lb_balancer.c lb_config.c 5 | 6 | NF_AUTOGEN_SRCS := lb_flow.h lb_backend.h ip_addr.h 7 | 8 | # CHT height must be a prime number 9 | NF_ARGS := --flow-expiration $(or $(EXPIRATION_TIME),10) \ 10 | --flow-capacity $(or $(CAPACITY),65536) \ 11 | --backend-capacity 32 \ 12 | --cht-height 97 \ 13 | --backend-expiration 3600000000 \ 14 | --wan $(or $(WAN_DEVICE), 2) 15 | 16 | NF_LAYER := 3 17 | 18 | NF_BENCH_NEEDS_REVERSE_TRAFFIC := true 19 | 20 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 21 | -------------------------------------------------------------------------------- /viglb/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {'backends.get' : {'constructor' : 'LoadBalancedBackendc', 2 | 'type' : 'LoadBalancedBackendi', 3 | 'fields' : ['nic', 'mac', 'ip']}} 4 | typeConstructors = {'ip_addrc' : 'ip_addri', 5 | 'LoadBalancedFlowc' : 'LoadBalancedFlowi'} 6 | stateObjects = {'flow_emap' : emap, 7 | 'flow_id_to_backend_id' : vector, 8 | 'backends' : vector, 9 | 'backend_ip_emap' : emap, 10 | 'cht' : None} 11 | -------------------------------------------------------------------------------- /viglb/ip_addr.h: -------------------------------------------------------------------------------- 1 | #ifndef _IP_ADDR_H_INCLUDED_ 2 | #define _IP_ADDR_H_INCLUDED_ 3 | 4 | #include 5 | 6 | struct ip_addr { 7 | uint32_t addr; 8 | }; 9 | 10 | #endif //_IP_ADDR_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /viglb/lb_backend.h: -------------------------------------------------------------------------------- 1 | #ifndef _LB_BACKEND_H_INCLUDED_ 2 | #define _LB_BACKEND_H_INCLUDED_ 3 | 4 | #include 5 | #include 6 | 7 | struct LoadBalancedBackend { 8 | uint16_t nic; 9 | struct rte_ether_addr mac; 10 | uint32_t ip; 11 | }; 12 | 13 | #endif //_LB_BACKEND_H_INCLUDED_ 14 | -------------------------------------------------------------------------------- /viglb/lb_balancer.h: -------------------------------------------------------------------------------- 1 | #ifndef _LB_BALANCER_H_INCLUDED_ // cannot use pragma once, included by VeriFast 2 | #define _LB_BALANCER_H_INCLUDED_ 3 | 4 | #include "libvig/verified/vector.h" 5 | #include "libvig/verified/double-chain.h" 6 | #include "libvig/verified/cht.h" 7 | 8 | #include 9 | 10 | #include "lb_flow.h.gen.h" 11 | #include "lb_backend.h.gen.h" 12 | #include "ip_addr.h.gen.h" 13 | 14 | struct LoadBalancer; 15 | struct LoadBalancer *lb_allocate_balancer(uint32_t flow_capacity, 16 | uint32_t backend_capacity, 17 | uint32_t cht_height, 18 | vigor_time_t backend_expiration_time, 19 | vigor_time_t flow_expiration_time); 20 | struct LoadBalancedBackend lb_get_backend(struct LoadBalancer *balancer, 21 | struct LoadBalancedFlow *flow, 22 | vigor_time_t now, 23 | uint16_t wan_device); 24 | void lb_expire_flows(struct LoadBalancer *balancer, vigor_time_t now); 25 | void lb_expire_backends(struct LoadBalancer *balancer, vigor_time_t now); 26 | void lb_process_heartbit(struct LoadBalancer *balancer, 27 | struct LoadBalancedFlow *flow, 28 | struct rte_ether_addr mac_addr, int nic, vigor_time_t now); 29 | 30 | #endif // _LB_BALANCER_H_INCLUDED_ 31 | -------------------------------------------------------------------------------- /viglb/lb_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include "nf.h" 7 | 8 | struct nf_config { 9 | // Number of backends 10 | uint16_t backend_count; 11 | 12 | // MAC addresses of the devices the backends are connected to 13 | struct rte_ether_addr *device_macs; 14 | 15 | // Size of the flow table 16 | uint32_t flow_capacity; 17 | 18 | // Expiration time of flows in microseconds 19 | uint32_t flow_expiration_time; 20 | 21 | // The maximum number of backends we can ballance at the same time 22 | uint32_t backend_capacity; 23 | 24 | // The height of the consistent hashing table. 25 | // Bigger value induces more memory usage, but can achieve finer 26 | // granularity. 27 | uint32_t cht_height; 28 | 29 | // The time in microseconds for which the load balancer is willing to wait 30 | // hoping to get another heartbeat. If no heartbeat comes for a host for this 31 | // time, it is considered down and removed from the pool of backends. 32 | uint32_t backend_expiration_time; 33 | 34 | // WAN device, i.e. external 35 | uint16_t wan_device; 36 | }; 37 | -------------------------------------------------------------------------------- /viglb/lb_flow.h: -------------------------------------------------------------------------------- 1 | #ifndef _LB_FLOW_H_INCLUDED_ 2 | #define _LB_FLOW_H_INCLUDED_ 3 | 4 | #include 5 | 6 | struct LoadBalancedFlow { 7 | uint32_t src_ip; 8 | uint32_t dst_ip; 9 | uint16_t src_port; 10 | uint16_t dst_port; 11 | uint8_t protocol; 12 | }; 13 | 14 | #endif //_LB_FLOW_H_INCLUDED_ 15 | -------------------------------------------------------------------------------- /viglb/paygo-alloc_flow_consistent.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, flow_id_to_backend_id, backends, backend_ip_emap, cht 2 | EXP_TIME = 10 * 1000 3 | BACKEND_EXP_TIME = 3600000000 * 1000 4 | EXT_PORT = 2 5 | 6 | if a_packet_received: 7 | flow_emap.expire_all(now - EXP_TIME) 8 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 9 | 10 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 11 | h2 = pop_header(ipv4, on_mismatch=([],[])) 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | packet_flow = LoadBalancedFlowc(h2.saddr, h2.daddr, h3.src_port, h3.dst_port, h2.npid) 15 | if received_on_port == EXT_PORT: 16 | if (not flow_emap.has(packet_flow) and 17 | backend_ip_emap.exists_with_cht(cht, _LoadBalancedFlow_hash(packet_flow)) and 18 | not flow_emap.full()): 19 | bknd = backend_ip_emap.choose_with_cht(cht, _LoadBalancedFlow_hash(packet_flow)) 20 | idx = the_index_allocated 21 | flow_emap.add(packet_flow, idx, now) 22 | flow_id_to_backend_id.set(idx, bknd) 23 | else: 24 | pass 25 | else: 26 | pass 27 | -------------------------------------------------------------------------------- /viglb/paygo-expired_backend_realloc_flow.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, flow_id_to_backend_id, backends, backend_ip_emap, cht 2 | EXP_TIME = 10 * 1000 3 | BACKEND_EXP_TIME = 3600000000 * 1000 4 | EXT_PORT = 2 5 | 6 | if a_packet_received: 7 | flow_emap.expire_all(now - EXP_TIME) 8 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 9 | 10 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 11 | h2 = pop_header(rte_ipv4, on_mismatch=([],[])) 12 | h1 = pop_header(rte_ether, on_mismatch=([],[])) 13 | 14 | packet_flow = LoadBalancedFlowc(h2.saddr, h2.daddr, h3.src_port, h3.dst_port, h2.npid) 15 | if received_on_port == EXT_PORT and flow_emap.has(packet_flow): 16 | backend_id = flow_id_to_backend_id.get(flow_emap.get(packet_flow)) 17 | if not backend_ip_emap.has_idx(backend_id): 18 | flow_emap.erase(packet_flow) 19 | if backend_ip_emap.exists_with_cht(cht, _LoadBalancedFlow_hash(packet_flow)): 20 | bknd = backend_ip_emap.choose_with_cht(cht, _LoadBalancedFlow_hash(packet_flow)) 21 | idx = the_index_allocated 22 | flow_emap.add(packet_flow, idx, now) 23 | flow_id_to_backend_id.set(idx, bknd) 24 | else: 25 | pass 26 | else: 27 | pass 28 | else: 29 | pass 30 | -------------------------------------------------------------------------------- /viglb/paygo-heartbeat_add_backend.py: -------------------------------------------------------------------------------- 1 | from state import backends, backend_ip_emap 2 | BACKEND_EXP_TIME = 3600000000 * 1000 3 | EXT_PORT = 2 4 | 5 | if a_packet_received: 6 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | bknd_addr = ip_addrc(h2.saddr) 13 | if (received_on_port != EXT_PORT and # A heartbeat from a backend 14 | not backend_ip_emap.has(bknd_addr) and 15 | not backend_ip_emap.full()): 16 | idx = the_index_allocated 17 | backend_ip_emap.add(bknd_addr, idx, now) 18 | backends.set(idx, LoadBalancedBackendc(received_on_port, h1.saddr, h2.saddr)) 19 | else: 20 | pass 21 | -------------------------------------------------------------------------------- /viglb/paygo-heartbeat_no_reply.py: -------------------------------------------------------------------------------- 1 | EXT_PORT = 2 2 | if received_on_port != EXT_PORT: # Heartbeat 3 | return ([],[]) 4 | -------------------------------------------------------------------------------- /viglb/paygo-heartbeat_refresh.py: -------------------------------------------------------------------------------- 1 | from state import backend_ip_emap 2 | BACKEND_EXP_TIME = 3600000000 * 1000 3 | EXT_PORT = 2 4 | 5 | if a_packet_received: 6 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | 12 | bknd_addr = ip_addrc(h2.saddr) 13 | if received_on_port != EXT_PORT and backend_ip_emap.has(bknd_addr): 14 | backend_ip_emap.refresh_idx(backend_ip_emap.get(bknd_addr), now) 15 | else: 16 | pass 17 | -------------------------------------------------------------------------------- /viglb/paygo-known_flow_forward.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, flow_id_to_backend_id, backends, backend_ip_emap 2 | EXP_TIME = 10 * 1000 3 | BACKEND_EXP_TIME = 3600000000 * 1000 4 | EXT_PORT = 2 5 | 6 | if a_packet_received: 7 | flow_emap.expire_all(now - EXP_TIME) 8 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 9 | 10 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 11 | h2 = pop_header(ipv4, on_mismatch=([],[])) 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | packet_flow = LoadBalancedFlowc(h2.saddr, h2.daddr, h3.src_port, h3.dst_port, h2.npid) 15 | if received_on_port == EXT_PORT and flow_emap.has(packet_flow): 16 | flow_id = flow_emap.get(packet_flow) 17 | backend_id = flow_id_to_backend_id.get(flow_id) 18 | if backend_ip_emap.has_idx(backend_id): 19 | flow_emap.refresh_idx(flow_emap.get(packet_flow), now) 20 | backend = backends.get(backend_id) 21 | return ([backend.nic], 22 | [ether(h1, saddr=..., daddr=backend.mac), 23 | ipv4(h2, cksum=..., daddr=backend.ip), 24 | tcpudp(h3)]) 25 | else: 26 | pass 27 | else: 28 | pass 29 | -------------------------------------------------------------------------------- /viglb/paygo-unknown_flow_consistent.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, backends, backend_ip_emap, cht 2 | EXP_TIME = 10 * 1000 3 | BACKEND_EXP_TIME = 3600000000 * 1000 4 | EXT_PORT = 2 5 | 6 | if a_packet_received: 7 | flow_emap.expire_all(now - EXP_TIME) 8 | backend_ip_emap.expire_all(now - BACKEND_EXP_TIME) 9 | 10 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 11 | h2 = pop_header(ipv4, on_mismatch=([],[])) 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | packet_flow = LoadBalancedFlowc(h2.saddr, h2.daddr, h3.src_port, h3.dst_port, h2.npid) 15 | if (received_on_port == EXT_PORT and 16 | not flow_emap.has(packet_flow) and 17 | backend_ip_emap.exists_with_cht(cht, _LoadBalancedFlow_hash(packet_flow))): 18 | bknd = backend_ip_emap.choose_with_cht(cht, _LoadBalancedFlow_hash(packet_flow)) 19 | backend = backends.get(bknd) 20 | return ([backend.nic], 21 | [ether(h1, saddr=..., daddr=backend.mac), 22 | ipv4(h2, cksum=..., daddr=backend.ip), 23 | tcpudp(h3)]) 24 | pass # This allows us to ignore the state changes 25 | else: 26 | pass 27 | -------------------------------------------------------------------------------- /vignat/Makefile: -------------------------------------------------------------------------------- 1 | NF_FILES := nat_main.c nat_config.c nat_flowmanager.c 2 | 3 | NF_AUTOGEN_SRCS := flow.h 4 | 5 | NF_ARGS := --wan 1 \ 6 | --expire $(or $(EXPIRATION_TIME),10) \ 7 | --starting-port 0 \ 8 | --max-flows $(or $(CAPACITY),65536) \ 9 | --extip $(or $(MB_IP_EXTERNAL),0.0.0.0) \ 10 | --eth-dest 0,$(or $(TESTER_MAC_EXTERNAL),01:23:45:67:89:00) \ 11 | --eth-dest 1,$(or $(TESTER_MAC_INTERNAL),01:23:45:67:89:01) 12 | 13 | NF_LAYER := 4 14 | 15 | 16 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 17 | -------------------------------------------------------------------------------- /vignat/dataspec.ml: -------------------------------------------------------------------------------- 1 | open Data_spec 2 | open Core 3 | open Ir 4 | 5 | let containers = ["fm", Map ("FlowId", "max_flows", ""); 6 | "fv", Vector ("FlowId", "max_flows", "flow_consistency"); 7 | "heap", DChain "max_flows"; 8 | "max_flows", Int; 9 | "start_port", Int; 10 | "ext_ip", UInt32; 11 | "nat_device", UInt32; 12 | "flow_emap", EMap ("FlowId", "fm", "fv", "heap")] 13 | 14 | let constraints = ["flow_consistency", ( "FlowId", 15 | [Bop (Le, {t=Unknown;v=Int 0}, {t=Unknown;v=Id "internal_device"}); 16 | Bop (Lt, {t=Unknown;v=Id "internal_device"}, {t=Unknown;v=Int 2}); 17 | Not {v=Bop (Eq, {t=Unknown;v=Id "internal_device"}, {t=Unknown;v=Int 1}); 18 | t=Unknown}; 19 | ])] 20 | 21 | let gen_custom_includes = ref [] 22 | let gen_records = ref [] 23 | -------------------------------------------------------------------------------- /vignat/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {'flow_emap.get_key' : {'constructor' : 'FlowIdc', 2 | 'type' : 'FlowIdi', 3 | 'fields' : ['sp', 'dp', 'sip', 'dip', 4 | 'idev', 'prot']}} 5 | typeConstructors = {'FlowIdc' : 'FlowIdi', 6 | 'emap' : 'emap'} 7 | stateObjects = {'flow_emap' : emap} 8 | -------------------------------------------------------------------------------- /vignat/flow.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct FlowId { 4 | uint16_t src_port; 5 | uint16_t dst_port; 6 | uint32_t src_ip; 7 | uint32_t dst_ip; 8 | uint16_t internal_device; 9 | uint8_t protocol; 10 | }; 11 | -------------------------------------------------------------------------------- /vignat/fspec.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Str 3 | open Fspec_api 4 | open Ir 5 | open Common_fspec 6 | 7 | let flow_id_struct = Ir.Str ( "FlowId", ["src_port", Uint16; 8 | "dst_port", Uint16; 9 | "src_ip", Uint32; 10 | "dst_ip", Uint32; 11 | "internal_device", Uint16; 12 | "protocol", Uint8;] ) 13 | 14 | module Iface : Fspec_api.Spec = 15 | struct 16 | (* FIXME: borrowed from ../nf/vignat/nat_data_spec.ml *) 17 | let containers = ["fm", Map ("FlowId", "max_flows", ""); 18 | "fv", Vector ("FlowId", "max_flows", "flow_consistency"); 19 | "heap", DChain "max_flows"; 20 | "max_flows", Int; 21 | "start_port", Int; 22 | "ext_ip", UInt32; 23 | "nat_device", UInt32; 24 | "flow_emap", EMap ("FlowId", "fm", "fv", "heap")] 25 | 26 | let records = String.Map.of_alist_exn 27 | ["FlowId", flow_id_struct] 28 | end 29 | 30 | (* Register the module *) 31 | let () = 32 | Fspec_api.spec := Some (module Iface) ; 33 | 34 | -------------------------------------------------------------------------------- /vignat/nat_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct nf_config { 8 | // "Main" LAN (i.e. internal) device, used for no-op not for NAT 9 | uint16_t lan_main_device; 10 | 11 | // WAN device, i.e. external 12 | uint16_t wan_device; 13 | 14 | // External IP address 15 | uint32_t external_addr; 16 | 17 | // MAC addresses of devices 18 | struct rte_ether_addr *device_macs; 19 | 20 | // MAC addresses of the endpoints the devices are linked to 21 | struct rte_ether_addr *endpoint_macs; 22 | 23 | // External port at which to start allocating flows 24 | // i.e. ports will be allocated in [start_port, start_port + max_flows] 25 | uint16_t start_port; 26 | 27 | // Expiration time of flows in microseconds 28 | uint32_t expiration_time; 29 | 30 | // Size of the flow table 31 | uint32_t max_flows; 32 | }; 33 | -------------------------------------------------------------------------------- /vignat/nat_flowmanager.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLOWMANAGER_H_INCLUDED_ 2 | #define _FLOWMANAGER_H_INCLUDED_ 3 | 4 | #include "flow.h.gen.h" 5 | #include "libvig/verified/vigor-time.h" 6 | 7 | #include 8 | #include 9 | 10 | struct FlowManager; 11 | 12 | struct FlowManager * 13 | flow_manager_allocate(uint16_t starting_port, uint32_t nat_ip, 14 | uint16_t nat_device, /* NOTE: only required for verif to 15 | show that internal != external; 16 | can be removed once "our NAT" == 17 | router + "only NAT" */ 18 | uint32_t expiration_time, uint64_t max_flows); 19 | 20 | bool flow_manager_allocate_flow(struct FlowManager *manager, struct FlowId *id, 21 | uint16_t internal_device, vigor_time_t time, 22 | uint16_t *external_port); 23 | void flow_manager_expire(struct FlowManager *manager, vigor_time_t time); 24 | bool flow_manager_get_internal(struct FlowManager *manager, struct FlowId *id, 25 | vigor_time_t time, uint16_t *external_port); 26 | bool flow_manager_get_external(struct FlowManager *manager, 27 | uint16_t external_port, vigor_time_t time, 28 | struct FlowId *out_flow); 29 | #endif //_FLOWMANAGER_H_INCLUDED_ 30 | -------------------------------------------------------------------------------- /vignat/paygo-block_packet_injection.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | flow_indx = h3.dst_port - start_port 12 | if received_on_port == EXT_PORT and flow_emap.has_idx(flow_indx): 13 | internal_flow = flow_emap.get_key(flow_indx) 14 | flow_emap.refresh_idx(flow_indx, now) 15 | if (internal_flow.dip != h2.saddr or 16 | internal_flow.dp != h3.src_port or 17 | internal_flow.prot != h2.npid): 18 | return ([],[]) 19 | else: 20 | pass 21 | else: 22 | pass 23 | -------------------------------------------------------------------------------- /vignat/paygo-block_unsolicited.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | flow_indx = h3.dst_port - start_port 12 | if received_on_port == EXT_PORT and not flow_emap.has_idx(flow_indx): 13 | return ([],[]) 14 | else: 15 | pass 16 | -------------------------------------------------------------------------------- /vignat/paygo-ext_no_flow_alloc.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | if a_packet_received: 5 | flow_emap.expire_all(now - EXP_TIME) 6 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 7 | h2 = pop_header(ipv4, on_mismatch=([],[])) 8 | h1 = pop_header(ether, on_mismatch=([],[])) 9 | 10 | if received_on_port == EXT_PORT: 11 | flow_indx = h3.dst_port - start_port 12 | if flow_emap.has_idx(flow_indx): # Flow is present in the table 13 | flow_emap.refresh_idx(flow_indx, now) 14 | else: # packet from the internal network 15 | pass 16 | -------------------------------------------------------------------------------- /vignat/paygo-forward_reply.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | 8 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 9 | h2 = pop_header(ipv4, on_mismatch=([],[])) 10 | h1 = pop_header(ether, on_mismatch=([],[])) 11 | flow_indx = h3.dst_port - start_port 12 | if received_on_port == EXT_PORT and flow_emap.has_idx(flow_indx): 13 | internal_flow = flow_emap.get_key(flow_indx) 14 | flow_emap.refresh_idx(flow_indx, now) 15 | if (internal_flow.dip == h2.saddr and 16 | internal_flow.dp == h3.src_port and 17 | internal_flow.prot == h2.npid): 18 | return ([internal_flow.idev], 19 | [ether(h1, saddr=..., daddr=...), 20 | ipv4(h2, cksum=..., saddr=internal_flow.dip, daddr=internal_flow.sip), 21 | tcpudp(src_port=internal_flow.dp, dst_port=internal_flow.sp)]) 22 | else: 23 | pass 24 | else: # packet from the internal network 25 | pass 26 | -------------------------------------------------------------------------------- /vignat/paygo-int_alloc_flow.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | if a_packet_received: 5 | flow_emap.expire_all(now - EXP_TIME) 6 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 7 | h2 = pop_header(ipv4, on_mismatch=([],[])) 8 | h1 = pop_header(ether, on_mismatch=([],[])) 9 | 10 | internal_flow_id = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, received_on_port, h2.npid) 11 | if (received_on_port != EXT_PORT and 12 | not flow_emap.has(internal_flow_id) and 13 | not flow_emap.full()): 14 | idx = the_index_allocated 15 | flow_emap.add(internal_flow_id, idx, now) 16 | else: 17 | pass 18 | -------------------------------------------------------------------------------- /vignat/paygo-int_refresh_flow.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | if a_packet_received: 5 | flow_emap.expire_all(now - EXP_TIME) 6 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 7 | h2 = pop_header(ipv4, on_mismatch=([],[])) 8 | h1 = pop_header(ether, on_mismatch=([],[])) 9 | 10 | internal_flow_id = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, received_on_port, h2.npid) 11 | if received_on_port != EXT_PORT and flow_emap.has(internal_flow_id) : 12 | flow_emap.refresh_idx(flow_emap.get(internal_flow_id), now) 13 | else: 14 | pass 15 | -------------------------------------------------------------------------------- /vignat/paygo-known_flow_refresh.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_IP_ADDR = ext_ip 4 | EXT_PORT = 1 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 8 | h2 = pop_header(ipv4, on_mismatch=([],[])) 9 | h1 = pop_header(ether, on_mismatch=([],[])) 10 | 11 | internal_flow_id = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, received_on_port, h2.npid) 12 | if received_on_port != EXT_PORT and flow_emap.has(internal_flow_id) : 13 | idx = flow_emap.get(internal_flow_id) 14 | flow_emap.refresh_idx(idx, now) 15 | return ([EXT_PORT], 16 | [ether(h1, saddr=..., daddr=...), 17 | ipv4(h2, cksum=..., saddr=EXT_IP_ADDR), 18 | tcpudp(h3, src_port=idx + start_port)]) 19 | else: 20 | pass 21 | -------------------------------------------------------------------------------- /vignat/paygo-new_flow_forward.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_IP_ADDR = ext_ip 4 | EXT_PORT = 1 5 | if a_packet_received: 6 | flow_emap.expire_all(now - EXP_TIME) 7 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 8 | h2 = pop_header(ipv4, on_mismatch=([],[])) 9 | h1 = pop_header(ether, on_mismatch=([],[])) 10 | 11 | internal_flow_id = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, received_on_port, h2.npid) 12 | if (received_on_port != EXT_PORT and 13 | not flow_emap.has(internal_flow_id) and 14 | not flow_emap.full()): 15 | idx = the_index_allocated 16 | flow_emap.add(internal_flow_id, idx, now) 17 | return ([EXT_PORT], 18 | [ether(h1, saddr=..., daddr=...), 19 | ipv4(h2, cksum=..., saddr=EXT_IP_ADDR), 20 | tcpudp(h3, src_port=idx + start_port)]) 21 | else: 22 | pass 23 | -------------------------------------------------------------------------------- /vignat/paygo-table_updates.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap 2 | EXP_TIME = 10 * 1000 3 | EXT_PORT = 1 4 | if a_packet_received: 5 | flow_emap.expire_all(now - EXP_TIME) 6 | h3 = pop_header(tcpudp, on_mismatch=([],[])) 7 | h2 = pop_header(ipv4, on_mismatch=([],[])) 8 | h1 = pop_header(ether, on_mismatch=([],[])) 9 | 10 | if received_on_port == EXT_PORT: 11 | flow_indx = h3.dst_port - start_port 12 | if flow_emap.has_idx(flow_indx): # Flow is present in the table 13 | flow_emap.refresh_idx(flow_indx, now) 14 | else: # packet from the internal network 15 | internal_flow_id = FlowIdc(h3.src_port, h3.dst_port, h2.saddr, h2.daddr, received_on_port, h2.npid) 16 | if flow_emap.has(internal_flow_id): # flow present in the table 17 | flow_emap.refresh_idx(flow_emap.get(internal_flow_id), now) 18 | elif not flow_emap.full(): # No flow in the table, but some space 19 | idx = the_index_allocated 20 | flow_emap.add(internal_flow_id, idx, now) 21 | -------------------------------------------------------------------------------- /vignop/Makefile: -------------------------------------------------------------------------------- 1 | NF_FILES := nop_main.c nat_config.c 2 | 3 | NF_ARGS := --lan-dev 0 \ 4 | --wan 1 \ 5 | --eth-dest 0,$(or $(TESTER_MAC_EXTERNAL),01:23:45:67:89:00) \ 6 | --eth-dest 1,$(or $(TESTER_MAC_INTERNAL),01:23:45:67:89:01) 7 | 8 | NF_LAYER := 4 9 | 10 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 11 | -------------------------------------------------------------------------------- /vignop/dataspec.ml: -------------------------------------------------------------------------------- 1 | open Data_spec 2 | open Core 3 | open Ir 4 | 5 | let containers = [] 6 | let custom_includes = [] 7 | 8 | let constraints = [] 9 | let gen_custom_includes : string list ref = ref [] 10 | let gen_records : (string * ttype) list ref = ref [] 11 | -------------------------------------------------------------------------------- /vignop/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {} 2 | typeConstructors = {} 3 | stateObjects = {} 4 | -------------------------------------------------------------------------------- /vignop/fspec.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Str 3 | open Fspec_api 4 | open Ir 5 | open Common_fspec 6 | 7 | module Iface : Fspec_api.Spec = 8 | struct 9 | let containers = [] 10 | let records = String.Map.of_alist_exn [] 11 | end 12 | 13 | let () = Fspec_api.spec := Some (module Iface) ; 14 | -------------------------------------------------------------------------------- /vignop/nat_config.c: -------------------------------------------------------------------------------- 1 | ../vignat/nat_config.c -------------------------------------------------------------------------------- /vignop/nat_config.h: -------------------------------------------------------------------------------- 1 | ../vignat/nat_config.h -------------------------------------------------------------------------------- /vignop/nop_main.c: -------------------------------------------------------------------------------- 1 | #include "nat_config.h" 2 | #include "nf.h" 3 | #include "nf-util.h" 4 | 5 | struct nf_config config; 6 | 7 | bool nf_init(void) { 8 | return true; 9 | } 10 | 11 | int nf_process(uint16_t device, uint8_t* buffer, uint16_t packet_length, vigor_time_t now) { 12 | // Mark now as unused, we don't care about time 13 | (void)now; 14 | 15 | // This is a bit of a hack; the benchmarks are designed for a NAT, which knows 16 | // where to forward packets, but for a plain forwarding app without any logic, 17 | // we just send all packets from LAN to the WAN port, and all packets from WAN 18 | // to the main LAN port, and let the recipient ignore the useless ones. 19 | 20 | uint16_t dst_device; 21 | if (device == config.wan_device) { 22 | dst_device = config.lan_main_device; 23 | } else { 24 | dst_device = config.wan_device; 25 | } 26 | 27 | // L2 forwarding 28 | struct rte_ether_hdr *rte_ether_header = nf_then_get_rte_ether_header(buffer); 29 | rte_ether_header->s_addr = config.device_macs[dst_device]; 30 | rte_ether_header->d_addr = config.endpoint_macs[dst_device]; 31 | 32 | return dst_device; 33 | } 34 | -------------------------------------------------------------------------------- /vignop/spec.py: -------------------------------------------------------------------------------- 1 | h = pop_header(ether, on_mismatch=([],[])) 2 | return ([1 - received_on_port], 3 | [ether(h, saddr=..., daddr=...)]) 4 | -------------------------------------------------------------------------------- /vigpol/Makefile: -------------------------------------------------------------------------------- 1 | NF_FILES := policer_main.c policer_config.c 2 | 3 | NF_AUTOGEN_SRCS := dynamic_value.h ip_addr.h 4 | 5 | NF_ARGS := --wan 0 --lan 1 --rate $(or $(POLICER_RATE),375000000) --burst $(or $(POLICER_BURST),3750000000) --capacity $(or $(CAPACITY),65536) 6 | 7 | NF_LAYER := 3 8 | 9 | include $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../Makefile 10 | -------------------------------------------------------------------------------- /vigpol/bkp/dynamic_value.h.gen.h: -------------------------------------------------------------------------------- 1 | #ifndef _DynamicValue_GEN_H_INCLUDED_ 2 | #define _DynamicValue_GEN_H_INCLUDED_ 3 | 4 | #include 5 | #include "libvig/verified/boilerplate-util.h" 6 | 7 | #include "libvig/verified/ether.h" 8 | 9 | 10 | #include "dynamic_value.h" 11 | 12 | #define DEFAULT_DynamicValue DynamicValuec(0, 0) 13 | 14 | /*@ 15 | inductive DynamicValuei = DynamicValuec(uint64_t , int64_t ); @*/ 16 | 17 | /*@ 18 | predicate DynamicValuep(struct DynamicValue* ptr; DynamicValuei v) = 19 | struct_DynamicValue_padding(ptr) &*& 20 | ptr->bucket_size |-> ?bucket_size_f &*& 21 | ptr->bucket_time |-> ?bucket_time_f &*& 22 | v == DynamicValuec(bucket_size_f, bucket_time_f); @*/ 23 | 24 | /*@ 25 | fixpoint unsigned _DynamicValue_hash(DynamicValuei x) { 26 | switch(x) { case DynamicValuec(bucket_size_f, bucket_time_f): 27 | return crc32_hash(crc32_hash(0, (bucket_size_f&0xfffffffffff)) & 0xffffffff, (bucket_time_f&0xfffffffffff)) & 0xffffffff; 28 | } 29 | } @*/ 30 | 31 | unsigned DynamicValue_hash(void* obj); 32 | //@ requires [?f]DynamicValuep(obj, ?v); 33 | //@ ensures [f]DynamicValuep(obj, v) &*& result == _DynamicValue_hash(v); 34 | 35 | bool DynamicValue_eq(void* a, void* b); 36 | //@ requires [?f1]DynamicValuep(a, ?aid) &*& [?f2]DynamicValuep(b, ?bid); 37 | /*@ ensures [f1]DynamicValuep(a, aid) &*& [f2]DynamicValuep(b, bid) &*& 38 | (result ? aid == bid : aid != bid); @*/ 39 | 40 | void DynamicValue_allocate(void* obj); 41 | //@ requires chars(obj, sizeof(struct DynamicValue), _); 42 | //@ ensures DynamicValuep(obj, DEFAULT_DynamicValue); 43 | 44 | #define LOG_DYNAMICVALUE(obj, p); \ 45 | p("{"); \ 46 | p("bucket_size: %d", obj->bucket_size); \ 47 | p("bucket_time: %d", obj->bucket_time); \ 48 | p("}"); 49 | 50 | 51 | #ifdef KLEE_VERIFICATION 52 | # include 53 | # include "libvig/models/str-descr.h" 54 | 55 | extern struct str_field_descr DynamicValue_descrs[2]; 56 | extern struct nested_field_descr DynamicValue_nests[0]; 57 | #endif//KLEE_VERIFICATION 58 | 59 | #endif//_DynamicValue_GEN_H_INCLUDED_ 60 | -------------------------------------------------------------------------------- /vigpol/bkp/ip_addr.h.gen.h: -------------------------------------------------------------------------------- 1 | #ifndef _ip_addr_GEN_H_INCLUDED_ 2 | #define _ip_addr_GEN_H_INCLUDED_ 3 | 4 | #include 5 | #include "libvig/verified/boilerplate-util.h" 6 | 7 | #include "libvig/verified/ether.h" 8 | 9 | 10 | #include "ip_addr.h" 11 | 12 | #define DEFAULT_ip_addr ip_addrc(0) 13 | 14 | /*@ 15 | inductive ip_addri = ip_addrc(uint32_t ); @*/ 16 | 17 | /*@ 18 | predicate ip_addrp(struct ip_addr* ptr; ip_addri v) = 19 | struct_ip_addr_padding(ptr) &*& 20 | ptr->addr |-> ?addr_f &*& 21 | v == ip_addrc(addr_f); @*/ 22 | 23 | /*@ 24 | fixpoint unsigned _ip_addr_hash(ip_addri x) { 25 | switch(x) { case ip_addrc(addr_f): 26 | return crc32_hash(0, addr_f); 27 | } 28 | } @*/ 29 | 30 | unsigned ip_addr_hash(void* obj); 31 | //@ requires [?f]ip_addrp(obj, ?v); 32 | //@ ensures [f]ip_addrp(obj, v) &*& result == _ip_addr_hash(v); 33 | 34 | bool ip_addr_eq(void* a, void* b); 35 | //@ requires [?f1]ip_addrp(a, ?aid) &*& [?f2]ip_addrp(b, ?bid); 36 | /*@ ensures [f1]ip_addrp(a, aid) &*& [f2]ip_addrp(b, bid) &*& 37 | (result ? aid == bid : aid != bid); @*/ 38 | 39 | void ip_addr_allocate(void* obj); 40 | //@ requires chars(obj, sizeof(struct ip_addr), _); 41 | //@ ensures ip_addrp(obj, ip_addrc(0)); 42 | 43 | #define LOG_IP_ADDR(obj, p); \ 44 | p("{"); \ 45 | p("addr: %d", obj->addr); \ 46 | p("}"); 47 | 48 | 49 | #ifdef KLEE_VERIFICATION 50 | # include 51 | # include "libvig/models/str-descr.h" 52 | 53 | extern struct str_field_descr ip_addr_descrs[1]; 54 | extern struct nested_field_descr ip_addr_nests[0]; 55 | #endif//KLEE_VERIFICATION 56 | 57 | #endif//_ip_addr_GEN_H_INCLUDED_ 58 | -------------------------------------------------------------------------------- /vigpol/dataspec.ml: -------------------------------------------------------------------------------- 1 | open Data_spec 2 | open Core 3 | open Ir 4 | 5 | let containers = ["dyn_map", Map ("ip_addr", "capacity", ""); 6 | "dyn_keys", Vector ("ip_addr", "capacity", ""); 7 | "dyn_heap", DChain "capacity"; 8 | "dyn_vals", Vector ("DynamicValue", "capacity", "dyn_val_condition"); 9 | "capacity", UInt32; 10 | "dev_count", UInt32; 11 | "flow_emap", EMap ("ip_addr", "dyn_map", "dyn_keys", "dyn_heap"); 12 | ] 13 | 14 | let constraints = ["dyn_val_condition", ( "DynamicValue", 15 | [Bop (Le, {t=Unknown;v=Int 0}, {t=Unknown;v=Id "bucket_time"}); 16 | Bop (Le, {t=Unknown;v=Id "bucket_time"}, {t=Unknown;v=Id "t"}); 17 | Bop (Le, {t=Unknown;v=Id "bucket_size"}, {t=Unknown;v=Int 3750000000}); 18 | ])] 19 | 20 | let gen_custom_includes = ref [] 21 | let gen_records = ref [] 22 | 23 | -------------------------------------------------------------------------------- /vigpol/dataspec.py: -------------------------------------------------------------------------------- 1 | objConstructors = {'dyn_vals.get' : {'constructor' : 'DynamicValuec', 2 | 'type' : 'DynamicValuei', 3 | 'fields' : ['bucket_size', 'bucket_time']}} 4 | typeConstructors = {'DynamicValuec' : 'DynamicValuei'} 5 | stateObjects = {'flow_emap' : emap, 6 | 'dyn_vals' : vector} 7 | -------------------------------------------------------------------------------- /vigpol/dynamic_value.h: -------------------------------------------------------------------------------- 1 | #ifndef _DYNAMIC_VALUE_H_INCLUDED_ 2 | #define _DYNAMIC_VALUE_H_INCLUDED_ 3 | 4 | #include "stdint.h" 5 | #include "libvig/verified/vigor-time.h" 6 | 7 | struct DynamicValue { 8 | uint64_t bucket_size; 9 | vigor_time_t bucket_time; 10 | }; 11 | 12 | #endif //_DYNAMIC_VALUE_H_INCLUDED_ 13 | -------------------------------------------------------------------------------- /vigpol/fspec.ml: -------------------------------------------------------------------------------- 1 | open Str 2 | open Core 3 | open Fspec_api 4 | open Ir 5 | open Common_fspec 6 | 7 | let ip_addr_struct = Ir.Str ( "ip_addr", ["addr", Uint32;]) 8 | let dynamic_value_struct = Ir.Str ( "DynamicValue", ["bucket_size", Uint64; 9 | "bucket_time", vigor_time_t;] ) 10 | (* TODO: make external_ip symbolic *) 11 | module Iface : Fspec_api.Spec = 12 | struct 13 | (* FIXME: borrowed from ../nf/vigpolicer/policer_data_spec.ml *) 14 | let containers = ["dyn_map", Map ("ip_addr", "capacity", ""); 15 | "dyn_keys", Vector ("ip_addr", "capacity", ""); 16 | "dyn_heap", DChain "capacity"; 17 | "dyn_vals", Vector ("DynamicValue", "capacity", "dyn_val_condition"); 18 | "capacity", UInt32; 19 | "dev_count", UInt32; 20 | "flow_emap", EMap ("ip_addr", "dyn_map", "dyn_keys", "dyn_heap"); 21 | ] 22 | 23 | let records = String.Map.of_alist_exn 24 | ["ip_addr", ip_addr_struct; 25 | "DynamicValue", dynamic_value_struct] 26 | end 27 | 28 | (* Register the module *) 29 | let () = 30 | Fspec_api.spec := Some (module Iface) ; 31 | 32 | -------------------------------------------------------------------------------- /vigpol/ip_addr.h: -------------------------------------------------------------------------------- 1 | #ifndef _IP_ADDR_H_INCLUDED_ 2 | #define _IP_ADDR_H_INCLUDED_ 3 | 4 | #include 5 | 6 | struct ip_addr { 7 | uint32_t addr; 8 | }; 9 | 10 | #endif //_IP_ADDR_H_INCLUDED_ 11 | -------------------------------------------------------------------------------- /vigpol/optimize.patch: -------------------------------------------------------------------------------- 1 | diff --git b/policer_main.c a/policer_main.c 2 | index 549aea75..768c4f32 100644 3 | --- b/policer_main.c 4 | +++ a/policer_main.c 5 | @@ -28,10 +28,9 @@ struct nf_config config; 6 | struct State *dynamic_ft; 7 | 8 | int policer_expire_entries(vigor_time_t time) { 9 | + assert(time >= 0); // we don't support the past 10 | vigor_time_t exp_time = 11 | VIGOR_TIME_SECONDS_MULTIPLIER * config.burst / config.rate; 12 | - if (time < exp_time) // we don't support the past 13 | - return 0; 14 | uint64_t time_u = (uint64_t)time; 15 | // OK because time >= config.burst / config.rate >= 0 16 | vigor_time_t min_time = time_u - exp_time; 17 | -------------------------------------------------------------------------------- /vigpol/paygo-drop_excess.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, dyn_vals 2 | WAN_DEVICE = 0 3 | BURST = 3750000000 4 | RATE = 375000000 5 | EXP_TIME = 10 * 1000 * 1000 * 1000 6 | 7 | h2 = pop_header(ipv4, on_mismatch=([],[])) 8 | # Malformed IPv4 9 | if (h2.vihl & 15) < 5 or packet_size - 14 < (((h2.len & 0xFF) << 8) | ((h2.len & 0xFF00) >> 8)): 10 | return ([],[]) 11 | 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | flow_emap.expire_all(now - EXP_TIME) 15 | 16 | if received_on_port == WAN_DEVICE and flow_emap.has(ip_addrc(h2.daddr)): 17 | flow_idx = flow_emap.get(ip_addrc(h2.daddr)) 18 | flow_emap.refresh_idx(flow_idx, now) 19 | flow = dyn_vals.get(flow_idx) 20 | bucket_size = flow.bucket_size + (now - flow.bucket_time)*RATE 21 | if BURST < bucket_size: 22 | bucket_size = BURST 23 | if bucket_size <= packet_size: 24 | dyn_vals.set(flow_idx, DynamicValuec(bucket_size, now)) 25 | return ([],[]) # The packet surpasses the limit 26 | else: 27 | pass 28 | else: 29 | pass 30 | -------------------------------------------------------------------------------- /vigpol/paygo-forward_compliant.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, dyn_vals 2 | LAN_DEVICE = 1 3 | WAN_DEVICE = 0 4 | BURST = 3750000000 5 | RATE = 375000000 6 | EXP_TIME = 10 * 1000 * 1000 * 1000 7 | 8 | h2 = pop_header(ipv4, on_mismatch=([],[])) 9 | # Malformed IPv4 10 | if (h2.vihl & 15) < 5 or packet_size - 14 < (((h2.len & 0xFF) << 8) | ((h2.len & 0xFF00) >> 8)): 11 | return ([],[]) 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | flow_emap.expire_all(now - EXP_TIME) 15 | 16 | if received_on_port == WAN_DEVICE and flow_emap.has(ip_addrc(h2.daddr)): 17 | flow_idx = flow_emap.get(ip_addrc(h2.daddr)) 18 | flow_emap.refresh_idx(flow_idx, now) 19 | flow = dyn_vals.get(flow_idx) 20 | bucket_size = flow.bucket_size + (now - flow.bucket_time)*RATE 21 | if BURST < bucket_size: 22 | bucket_size = BURST 23 | if packet_size < bucket_size: 24 | bucket_size = bucket_size - packet_size 25 | dyn_vals.set(flow_idx, DynamicValuec(bucket_size, now)) 26 | return ([LAN_DEVICE], [ether(h1),ipv4(h2)]) 27 | else: 28 | pass 29 | else: 30 | pass 31 | -------------------------------------------------------------------------------- /vigpol/paygo-incast_add.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, dyn_vals 2 | LAN_DEVICE = 1 3 | WAN_DEVICE = 0 4 | BURST = 3750000000 5 | RATE = 375000000 6 | EXP_TIME = 10 * 1000 * 1000 * 1000 7 | 8 | h2 = pop_header(ipv4, on_mismatch=([],[])) 9 | # Malformed IPv4 10 | if (h2.vihl & 15) < 5 or packet_size - 14 < (((h2.len & 0xFF) << 8) | ((h2.len & 0xFF00) >> 8)): 11 | return ([],[]) 12 | h1 = pop_header(ether, on_mismatch=([],[])) 13 | 14 | flow_emap.expire_all(now - EXP_TIME) 15 | 16 | if (received_on_port == WAN_DEVICE and 17 | not flow_emap.has(ip_addrc(h2.daddr)) and 18 | not flow_emap.full()): 19 | flow_idx = the_index_allocated 20 | flow_emap.add(ip_addrc(h2.daddr), flow_idx, now) 21 | flow = DynamicValuec(BURST - packet_size, now) 22 | dyn_vals.set(flow_idx, flow) 23 | return ([LAN_DEVICE], [ether(h1), ipv4(h2)]) 24 | else: 25 | pass 26 | -------------------------------------------------------------------------------- /vigpol/paygo-outcast_nopolicing.py: -------------------------------------------------------------------------------- 1 | LAN_DEVICE = 1 2 | WAN_DEVICE = 0 3 | 4 | h2 = pop_header(ipv4, on_mismatch=([],[])) 5 | # Malformed IPv4 6 | if (h2.vihl & 15) < 5 or packet_size - 14 < (((h2.len & 0xFF) << 8) | ((h2.len & 0xFF00) >> 8)): 7 | return ([],[]) 8 | h1 = pop_header(ether, on_mismatch=([],[])) 9 | 10 | if received_on_port == LAN_DEVICE: 11 | return ([WAN_DEVICE],[ether(h1), ipv4(h2)]) 12 | else: 13 | pass 14 | -------------------------------------------------------------------------------- /vigpol/policer_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "nf.h" 6 | 7 | #define CONFIG_FNAME_LEN 512 8 | 9 | struct nf_config { 10 | // LAN (i.e. internal) device 11 | uint16_t lan_device; 12 | 13 | // WAN device, i.e. external 14 | uint16_t wan_device; 15 | 16 | // Policer rate in B/s 17 | uint64_t rate; 18 | 19 | // Policer burst size in B 20 | uint64_t burst; 21 | 22 | // Size of the dynamic filtering table 23 | uint32_t dyn_capacity; 24 | }; 25 | -------------------------------------------------------------------------------- /vigpol/spec.py: -------------------------------------------------------------------------------- 1 | from state import flow_emap, dyn_vals 2 | LAN_DEVICE = 1 3 | WAN_DEVICE = 0 4 | BURST = 3750000000 5 | RATE = 375000000 6 | EXP_TIME = 10 * 1000 * 1000 * 1000 7 | 8 | h2 = pop_header(ipv4, on_mismatch=([],[])) 9 | h1 = pop_header(ether, on_mismatch=([],[])) 10 | 11 | # Malformed IPv4 12 | if (h2.vihl & 15) < 5 or packet_size - 14 < (((h2.len & 0xFF) << 8) | ((h2.len & 0xFF00) >> 8)): 13 | return ([],[]) 14 | 15 | flow_emap.expire_all(now - EXP_TIME) 16 | 17 | if received_on_port == LAN_DEVICE: 18 | return ([WAN_DEVICE],[ether(h1), ipv4(h2)]) 19 | else: 20 | if received_on_port == WAN_DEVICE: 21 | if flow_emap.has(ip_addrc(h2.daddr)): 22 | flow_idx = flow_emap.get(ip_addrc(h2.daddr)) 23 | flow_emap.refresh_idx(flow_idx, now) 24 | flow = dyn_vals.get(flow_idx) 25 | bucket_size = 0 26 | if now - flow.bucket_time < EXP_TIME: 27 | bucket_size = flow.bucket_size + (now - flow.bucket_time)*RATE / 1000000000 28 | if BURST < bucket_size: 29 | bucket_size = BURST 30 | else: 31 | bucket_size = BURST 32 | if packet_size < bucket_size: 33 | bucket_size = bucket_size - packet_size 34 | dyn_vals.set(flow_idx, DynamicValuec(bucket_size, now)) 35 | return ([LAN_DEVICE], [ether(h1),ipv4(h2)]) 36 | else: 37 | dyn_vals.set(flow_idx, DynamicValuec(bucket_size, now)) 38 | return ([],[]) 39 | else: 40 | if not flow_emap.full(): 41 | flow_idx = the_index_allocated 42 | flow_emap.add(ip_addrc(h2.daddr), flow_idx, now) 43 | flow = DynamicValuec(BURST - packet_size, now) 44 | dyn_vals.set(flow_idx, flow) 45 | return ([LAN_DEVICE], [ether(h1), ipv4(h2)]) 46 | else: 47 | return ([],[]) 48 | 49 | -------------------------------------------------------------------------------- /vigpol/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd) 6 | 7 | function cleanup { 8 | sudo killall policer 2>/dev/null || true 9 | sudo killall iperf 2>/dev/null || true 10 | sudo ip netns delete lan 2>/dev/null || true 11 | sudo ip netns delete wan 2>/dev/null || true 12 | } 13 | trap cleanup EXIT 14 | 15 | 16 | function test_policer { 17 | RATE=$1 18 | BURST=$2 19 | 20 | sudo taskset -c 8 \ 21 | ./build/app/policer \ 22 | --vdev "net_tap0,iface=test_wan" \ 23 | --vdev "net_tap1,iface=test_lan" \ 24 | --no-shconf -- \ 25 | --lan 3 \ 26 | --wan 2 \ 27 | --rate $RATE \ 28 | --burst $BURST \ 29 | --capacity 65536 \ 30 | >/dev/null 2>/dev/null & 31 | NF_PID=$! 32 | 33 | while [ ! -f /sys/class/net/test_lan/tun_flags -o 34 | ! -f /sys/class/net/test_lan/tun_flags ]; do 35 | echo "Waiting for NF to launch..."; 36 | sleep 1; 37 | done 38 | sleep 2 39 | 40 | sudo ip netns add lan 41 | sudo ip link set test_lan netns lan 42 | sudo ip netns exec lan ifconfig test_lan up 10.0.0.1 43 | LAN_MAC=$(sudo ip netns exec lan ifconfig test_lan | head -n 1 | awk '{ print $5 }') 44 | 45 | sudo ip netns add wan 46 | sudo ip link set test_wan netns wan 47 | sudo ip netns exec wan ifconfig test_wan up 10.0.0.2 48 | WAN_MAC=$(sudo ip netns exec wan ifconfig test_wan | head -n 1 | awk '{ print $5 }') 49 | 50 | sudo ip netns exec lan arp -i test_lan -s 10.0.0.2 $WAN_MAC 51 | sudo ip netns exec wan arp -i test_wan -s 10.0.0.1 $LAN_MAC 52 | 53 | sudo ip netns exec lan iperf -us -i 1 & 54 | SERVER_PID=$! 55 | 56 | sudo ip netns exec wan iperf -uc 10.0.0.1 -t 10 >/dev/null 57 | 58 | sudo killall iperf 59 | wait $SERVER_PID 2>/dev/null || true 60 | 61 | sudo killall policer 62 | wait $NF_PID 2>/dev/null || true 63 | 64 | sudo ip netns delete lan 65 | sudo ip netns delete wan 66 | } 67 | 68 | 69 | make clean 70 | make ADDITIONAL_FLAGS="-DSTOP_ON_RX_0 -g" -j$(nproc) 71 | 72 | test_policer 12500 500000 73 | 74 | echo "Done." 75 | --------------------------------------------------------------------------------