diff options
author | Deepak S <deepak.s@linux.intel.com> | 2017-07-13 21:26:50 -0700 |
---|---|---|
committer | Deepak S <deepak.s@linux.intel.com> | 2017-07-14 04:58:47 -0700 |
commit | 7286b2518ec8e4398b512ce95def9166a7af2e4a (patch) | |
tree | c93ef65d9e73e8893ccecb720152e16aae96a8b6 /VNFs/DPPD-PROX | |
parent | adcb79da90176b27224eeb1d00aa0e611ef85a9b (diff) |
Adding PROX(Packet pROcessing eXecution engine) VNF to sampleVNF
JIRA: SAMPLEVNF-55
PROX is a DPDK-based application implementing Telco use-cases such as
a simplified BRAS/BNG, light-weight AFTR... It also allows configuring
finer grained network functions like QoS, Routing, load-balancing...
(We are moving PROX version v039 to sampleVNF
https://01.org/intel-data-plane-performance-demonstrators/prox-overview)
Change-Id: Ia3cb02cf0e49ac5596e922c197ff7e010293d033
Signed-off-by: Deepak S <deepak.s@linux.intel.com>
Diffstat (limited to 'VNFs/DPPD-PROX')
362 files changed, 64273 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/LICENSE.ALv2 b/VNFs/DPPD-PROX/LICENSE.ALv2 new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/VNFs/DPPD-PROX/LICENSE.ALv2 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/VNFs/DPPD-PROX/Makefile b/VNFs/DPPD-PROX/Makefile new file mode 100644 index 00000000..0288181c --- /dev/null +++ b/VNFs/DPPD-PROX/Makefile @@ -0,0 +1,204 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overriden by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +rte_version_h := $(RTE_SDK)/$(RTE_TARGET)/include/rte_version.h +rte_ver_part = $(shell sed -n -e 's/^\#define\s*$1\s*\(.*\)$$/\1/p' $(rte_version_h)) +rte_ver_eval = $(shell printf '%u' $$(printf '0x%02x%02x%02x%02x' $1 $2 $3 $4)) +rte_ver_MMLR = $(call rte_ver_eval,$(call \ + rte_ver_part,RTE_VER_MAJOR),$(call \ + rte_ver_part,RTE_VER_MINOR),$(call \ + rte_ver_part,RTE_VER_PATCH_LEVEL),$(call \ + rte_ver_part,RTE_VER_PATCH_RELEASE)) +rte_ver_YMMR = $(call rte_ver_eval,$(call \ + rte_ver_part,RTE_VER_YEAR),$(call \ + rte_ver_part,RTE_VER_MONTH),$(call \ + rte_ver_part,RTE_VER_MINOR),$(call \ + rte_ver_part,RTE_VER_RELEASE)) +rte_ver_dpdk := $(if $(call rte_ver_part,RTE_VER_MAJOR),$(rte_ver_MMLR),$(rte_ver_YMMR)) +rte_ver_comp = $(shell test $(rte_ver_dpdk) $5 $(call rte_ver_eval,$1,$2,$3,$4) && echo 'y') +rte_ver_EQ = $(call rte_ver_comp,$1,$2,$3,$4,-eq) +rte_ver_NE = $(call rte_ver_comp,$1,$2,$3,$4,-ne) +rte_ver_GT = $(call rte_ver_comp,$1,$2,$3,$4,-gt) +rte_ver_LT = $(call rte_ver_comp,$1,$2,$3,$4,-lt) +rte_ver_GE = $(call rte_ver_comp,$1,$2,$3,$4,-ge) +rte_ver_LE = $(call rte_ver_comp,$1,$2,$3,$4,-le) + +include $(RTE_SDK)/mk/rte.vars.mk + +# binary name +APP = prox +CFLAGS += -DPROGRAM_NAME=\"$(APP)\" + +CFLAGS += -O2 -g +CFLAGS += -fno-stack-protector -Wno-deprecated-declarations + +ifeq ($(BNG_QINQ),) +CFLAGS += -DUSE_QINQ +else ifeq ($(BNG_QINQ),y) +CFLAGS += -DUSE_QINQ +endif + +ifeq ($(MPLS_ROUTING),) +CFLAGS += -DMPLS_ROUTING +else ifeq ($(MPLS_ROUTING),y) +CFLAGS += -DMPLS_ROUTING +endif + +LD_LUA = $(shell pkg-config --silence-errors --libs-only-l lua) +CFLAGS += $(shell pkg-config --silence-errors --cflags lua) +ifeq ($(LD_LUA),) +LD_LUA = $(shell pkg-config --silence-errors --libs-only-l lua5.2) +CFLAGS += $(shell pkg-config --silence-errors --cflags lua5.2) +ifeq ($(LD_LUA),) +LD_LUA = $(shell pkg-config --silence-errors --libs-only-l lua5.3) +CFLAGS += $(shell pkg-config --silence-errors --cflags lua5.3) +ifeq ($(LD_LUA),) +LD_LUA =-llua +endif +endif +endif + +LD_TINFO = $(shell pkg-config --silence-errors --libs-only-l tinfo) +LDFLAGS += -lpcap $(LD_TINFO) $(LD_LUA) +LDFLAGS += -lncurses -lncursesw -ledit + +PROX_STATS ?= y +ifeq ($(PROX_STATS),y) +CFLAGS += -DPROX_STATS +endif + +ifeq ($(DPI_STATS),y) +CFLAGS += -DDPI_STATS +endif + +ifeq ($(HW_DIRECT_STATS),y) +CFLAGS += -DPROX_HW_DIRECT_STATS +endif + +ifeq ($(dbg),y) +EXTRA_CFLAGS += -ggdb +endif + +ifeq ($(log),) +CFLAGS += -DPROX_MAX_LOG_LVL=2 +else +CFLAGS += -DPROX_MAX_LOG_LVL=$(log) +endif + +# override any use-case/enviroment specific choices regarding crc and +# always use the sw implementation +ifeq ($(crc),soft) +CFLAGS += -DSOFT_CRC +endif + +CFLAGS += -DPROX_PREFETCH_OFFSET=2 +#CFLAGS += -DBRAS_RX_BULK +#CFLAGS += -DASSERT +#CFLAGS += -DENABLE_EXTRA_USER_STATISTICS +CFLAGS += -DLATENCY_PER_PACKET +CFLAGS += -DLATENCY_DETAILS +CFLAGS += -DGRE_TP +CFLAGS += -std=gnu99 +CFLAGS += -D_GNU_SOURCE # for PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +CFLAGS += $(WERROR_FLAGS) +CFLAGS += -Wno-unused +CFLAGS += -Wno-unused-parameter +CFLAGS += -Wno-unused-result + +# all source are stored in SRCS-y + +SRCS-y := task_init.c + +SRCS-y += handle_aggregator.c +SRCS-y += handle_nop.c +SRCS-y += handle_irq.c +SRCS-y += handle_arp.c +SRCS-y += handle_impair.c +SRCS-y += handle_lat.c +SRCS-y += handle_qos.c +SRCS-y += handle_qinq_decap4.c +SRCS-y += handle_routing.c +SRCS-y += handle_untag.c +SRCS-y += handle_mplstag.c +SRCS-y += handle_qinq_decap6.c + +# support for GRE encap/decap dropped in latest DPDK versions +SRCS-$(call rte_ver_LT,2,1,0,0) += handle_gre_decap_encap.c + +SRCS-y += rw_reg.c +SRCS-y += handle_lb_qinq.c +SRCS-y += handle_lb_pos.c +SRCS-y += handle_lb_net.c +SRCS-y += handle_qinq_encap4.c +SRCS-y += handle_qinq_encap6.c +SRCS-y += handle_classify.c +SRCS-y += handle_l2fwd.c +SRCS-y += handle_swap.c +SRCS-y += handle_police.c +SRCS-y += handle_acl.c +SRCS-y += handle_gen.c +SRCS-y += handle_mirror.c +SRCS-y += handle_genl4.c +SRCS-y += handle_ipv6_tunnel.c +SRCS-y += handle_read.c +SRCS-y += handle_cgnat.c +SRCS-y += handle_nat.c +SRCS-y += handle_dump.c +SRCS-y += handle_tsc.c +SRCS-y += handle_fm.c +SRCS-$(call rte_ver_GE,1,8,0,16) += handle_nsh.c +SRCS-y += handle_lb_5tuple.c +SRCS-y += handle_blockudp.c +SRCS-y += toeplitz.c +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += handle_pf_acl.c + +SRCS-y += thread_nop.c +SRCS-y += thread_generic.c +SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_pipeline.c + +SRCS-y += prox_args.c prox_cfg.c prox_cksum.c prox_port_cfg.c + +SRCS-y += cfgfile.c clock.c commands.c cqm.c msr.c defaults.c +SRCS-y += display.c display_latency.c display_mempools.c +SRCS-y += display_ports.c display_rings.c display_priority.c display_pkt_len.c display_l4gen.c display_tasks.c +SRCS-y += log.c hash_utils.c main.c parse_utils.c file_utils.c +SRCS-y += run.c input_conn.c input_curses.c +SRCS-y += rx_pkt.c lconf.c tx_pkt.c expire_cpe.c ip_subnet.c +SRCS-y += stats_port.c stats_mempool.c stats_ring.c stats_l4gen.c +SRCS-y += stats_latency.c stats_global.c stats_core.c stats_task.c stats_prio.c +SRCS-y += cmd_parser.c input.c prox_shared.c prox_lua_types.c +SRCS-y += genl4_bundle.c heap.c genl4_stream_tcp.c genl4_stream_udp.c cdf.c +SRCS-y += stats.c stats_cons_log.c stats_cons_cli.c stats_parser.c hash_set.c prox_lua.c prox_malloc.c + +ifeq ($(FIRST_PROX_MAKE),) +MAKEFLAGS += --no-print-directory +FIRST_PROX_MAKE = 1 +export FIRST_PROX_MAKE +all: + @./helper-scripts/trailing.sh + @$(MAKE) $@ +%:: + @$(MAKE) $@ +else +include $(RTE_SDK)/mk/rte.extapp.mk +endif diff --git a/VNFs/DPPD-PROX/README b/VNFs/DPPD-PROX/README new file mode 100644 index 00000000..a09873cd --- /dev/null +++ b/VNFs/DPPD-PROX/README @@ -0,0 +1,117 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +Description +----------- +This is PROX, the Packet pROcessing eXecution engine, part of Intel(R) +Data Plane Performance Demonstrators, and formerly known as DPPD-BNG. +PROX is a DPDK-based application implementing Telco use-cases such as +a simplified BRAS/BNG, light-weight AFTR... It also allows configuring +finer grained network functions like QoS, Routing, load-balancing... + +Compiling and running this application +-------------------------------------- +This application supports DPDK 16.04, 16.11, 16.11.1, 17.02 and 17.05. +The following commands assume that the following variables have been set: + +export RTE_SDK=/path/to/dpdk +export RTE_TARGET=x86_64-native-linuxapp-gcc + +Example: DPDK 17.05 installation +-------------------------------- +git clone http://dpdk.org/git/dpdk +cd dpdk +git checkout v17.05 +make install T=$RTE_TARGET + +PROX compilation +---------------- +The Makefile with this application expects RTE_SDK to point to the +root directory of DPDK (e.g. export RTE_SDK=/root/dpdk). If RTE_TARGET +has not been set, x86_64-native-linuxapp-gcc will be assumed. + +Running PROX +------------ +After DPDK has been set up, run make from the directory where you have +extracted this application. A build directory will be created +containing the PROX executable. The usage of the application is shown +below. Note that this application assumes that all required ports have +been bound to the DPDK provided igb_uio driver. Refer to the "Getting +Started Guide - DPDK" document for more details. + +Usage: ./build/prox [-f CONFIG_FILE] [-l LOG_FILE] [-p] [-o DISPLAY] [-v] [-a|-e] \ + [-m|-s|-i] [-n] [-w DEF] [-q] [-k] [-d] [-z] [-r VAL] [-u] [-t] + -f CONFIG_FILE : configuration file to load, ./prox.cfg by default + -l LOG_FILE : log file name, ./prox.log by default + -p : include PID in log file name if default log file is used + -o DISPLAY: Set display to use, can be 'curses' (default), 'cli' or 'none' + -v verbosity : initial logging verbosity + -a : autostart all cores (by default) + -e : don't autostart + -n : Create NULL devices instead of using PCI devices, useful together with -i + -m : list supported task modes and exit + -s : check configuration file syntax and exit + -i : check initialization sequence and exit + -u : Listen on UDS /tmp/prox.sock + -t : Listen on TCP port 8474 + -q : Pass argument to Lua interpreter, useful to define variables + -w : define variable using syntax varname=value + takes precedence over variables defined in CONFIG_FILE + -k : Log statistics to file "stats_dump" in current directory + -d : Run as daemon, the parent process will block until PROX is not initialized + -z : Ignore CPU topology, implies -i + -r : Change initial screen refresh rate. If set to a lower than 0.001 seconds, + screen refreshing will be disabled + +While applications using DPDK typically rely on the core mask and the +number of channels to be specified on the command line, this +application is configured using a .cfg file. The core mask and number +of channels is derived from this config. For example, to run the +application from the source directory execute: + + user@target:~$ ./build/prox -f ./config/nop.cfg + +Provided example configurations +------------------------------- +PROX can be configured either as the SUT (System Under Test) or as the +Traffic Generator. Some example configuration files are provided, both +in the config directory to run PROX as a SUT, and in the gen directory +to run it as a Traffic Generator. +A quick description of these example configurations is provided below. +Additional details are provided in the example configuration files. + +Basic configurations, mostly used as sanity check: +- config/nop.cfg +- config/nop-rings.cfg +- gen/nop-gen.cfg + +Simplified BNG (Border Network Gateway) configurations, using different +number of ports, with and without QoS, running on the host or in a VM: +- config/bng-4ports.cfg +- config/bng-8ports.cfg +- config/bng-qos-4ports.cfg +- config/bng-qos-8ports.cfg +- config/bng-1q-4ports.cfg +- config/bng-ovs-usv-4ports.cfg +- config/bng-no-cpu-topology-4ports.cfg +- gen/bng-4ports-gen.cfg +- gen/bng-8ports-gen.cfg +- gen/bng-ovs-usv-4ports-gen.cfg + +Light-weight AFTR configurations: +- config/lw_aftr.cfg +- gen/lw_aftr-gen.cfg + diff --git a/VNFs/DPPD-PROX/acl_field_def.h b/VNFs/DPPD-PROX/acl_field_def.h new file mode 100644 index 00000000..ede5bea7 --- /dev/null +++ b/VNFs/DPPD-PROX/acl_field_def.h @@ -0,0 +1,152 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _ACL_FIELD_DEF_H_ +#define _ACL_FIELD_DEF_H_ + +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + +#include "qinq.h" + +struct pkt_eth_ipv4_udp { + struct ether_hdr ether_hdr; + struct ipv4_hdr ipv4_hdr; + struct udp_hdr udp_hdr; +} __attribute__((packed)); + +static struct rte_acl_field_def pkt_eth_ipv4_udp_defs[] = { + /* first input field - always one byte long. */ + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof (uint8_t), + .field_index = 0, + .input_index = 0, + .offset = offsetof (struct pkt_eth_ipv4_udp, ipv4_hdr.next_proto_id), + }, + /* IPv4 source address. */ + { + .type = RTE_ACL_FIELD_TYPE_MASK, + .size = sizeof (uint32_t), + .field_index = 1, + .input_index = 1, + .offset = offsetof (struct pkt_eth_ipv4_udp, ipv4_hdr.src_addr), + }, + /* IPv4 destination address */ + { + .type = RTE_ACL_FIELD_TYPE_MASK, + .size = sizeof (uint32_t), + .field_index = 2, + .input_index = 2, + .offset = offsetof (struct pkt_eth_ipv4_udp, ipv4_hdr.dst_addr), + }, + /* (L4 src/dst port) - 4 consecutive bytes. */ + { + .type = RTE_ACL_FIELD_TYPE_RANGE, + .size = sizeof (uint16_t), + .field_index = 3, + .input_index = 3, + .offset = offsetof (struct pkt_eth_ipv4_udp, udp_hdr.src_port), + }, + { + .type = RTE_ACL_FIELD_TYPE_RANGE, + .size = sizeof (uint16_t), + .field_index = 4, + .input_index = 3, + .offset = offsetof (struct pkt_eth_ipv4_udp, udp_hdr.dst_port), + }, +}; + +struct pkt_qinq_ipv4_udp { + struct qinq_hdr qinq_hdr; + struct ipv4_hdr ipv4_hdr; + struct udp_hdr udp_hdr; +}; + +static struct rte_acl_field_def pkt_qinq_ipv4_udp_defs[] = { + /* first input field - always one byte long. */ + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof (uint8_t), + .field_index = 0, + .input_index = 0, + .offset = offsetof (struct pkt_qinq_ipv4_udp, ipv4_hdr.next_proto_id), + }, + /* IPv4 source address. */ + { + .type = RTE_ACL_FIELD_TYPE_MASK, + .size = sizeof (uint32_t), + .field_index = 1, + .input_index = 1, + .offset = offsetof (struct pkt_qinq_ipv4_udp, ipv4_hdr.src_addr), + }, + /* IPv4 destination address */ + { + .type = RTE_ACL_FIELD_TYPE_MASK, + .size = sizeof (uint32_t), + .field_index = 2, + .input_index = 2, + .offset = offsetof (struct pkt_qinq_ipv4_udp, ipv4_hdr.dst_addr), + }, + /* (L4 src/dst port) - 4 consecutive bytes. */ + { + .type = RTE_ACL_FIELD_TYPE_RANGE, + .size = sizeof (uint16_t), + .field_index = 3, + .input_index = 3, + .offset = offsetof (struct pkt_qinq_ipv4_udp, udp_hdr.src_port), + }, + { + .type = RTE_ACL_FIELD_TYPE_RANGE, + .size = sizeof (uint16_t), + .field_index = 4, + .input_index = 3, + .offset = offsetof (struct pkt_qinq_ipv4_udp, udp_hdr.dst_port), + }, + /* (SVLAN id + eth type) - 4 consecutive bytes. */ + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof(uint16_t), + .field_index = 5, + .input_index = 4, + .offset = offsetof (struct pkt_qinq_ipv4_udp, qinq_hdr.svlan.eth_proto), + }, + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof(uint16_t), + .field_index = 6, + .input_index = 4, + .offset = offsetof (struct pkt_qinq_ipv4_udp, qinq_hdr.svlan.vlan_tci), + }, + /* (CVLAN id + eth type) - 4 consecutive byates. */ + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof(uint16_t), + .field_index = 7, + .input_index = 5, + .offset = offsetof (struct pkt_qinq_ipv4_udp, qinq_hdr.cvlan.eth_proto), + }, + { + .type = RTE_ACL_FIELD_TYPE_BITMASK, + .size = sizeof(uint16_t), + .field_index = 8, + .input_index = 5, + .offset = offsetof (struct pkt_qinq_ipv4_udp, qinq_hdr.cvlan.vlan_tci), + }, +}; + +#endif /* _ACL_FIELD_DEF_H_ */ diff --git a/VNFs/DPPD-PROX/arp.h b/VNFs/DPPD-PROX/arp.h new file mode 100644 index 00000000..279bdada --- /dev/null +++ b/VNFs/DPPD-PROX/arp.h @@ -0,0 +1,72 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _ARP_H_ +#define _ARP_H_ + +#include <rte_ether.h> + +#define ARP_REQUEST 0x100 +#define ARP_REPLY 0x200 + +struct _arp_ipv4 { + struct ether_addr sha; /* Sender hardware address */ + uint32_t spa; /* Sender protocol address */ + struct ether_addr tha; /* Target hardware address */ + uint32_t tpa; /* Target protocol address */ +} __attribute__((__packed__)); +typedef struct _arp_ipv4 arp_ipv4_t; + +struct my_arp_t { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t oper; + arp_ipv4_t data; +} __attribute__((__packed__)); + +struct ether_hdr_arp { + struct ether_hdr ether_hdr; + struct my_arp_t arp; +}; + +static int arp_is_gratuitous(struct ether_hdr_arp *hdr) +{ + return hdr->arp.data.spa == hdr->arp.data.tpa; +} + +static inline void prepare_arp_reply(struct ether_hdr_arp *hdr_arp, struct ether_addr *s_addr) +{ + uint32_t ip_source = hdr_arp->arp.data.spa; + + hdr_arp->arp.data.spa = hdr_arp->arp.data.tpa; + hdr_arp->arp.data.tpa = ip_source; + hdr_arp->arp.oper = 0x200; + memcpy(&hdr_arp->arp.data.tha, &hdr_arp->arp.data.sha, sizeof(struct ether_addr)); + memcpy(&hdr_arp->arp.data.sha, s_addr, sizeof(struct ether_addr)); +} + +static void create_mac(struct ether_hdr_arp *hdr, struct ether_addr *addr) +{ + addr->addr_bytes[0] = 0x2; + addr->addr_bytes[1] = 0; + // Instead of sending a completely random MAC address, create the following MAC: + // 02:00:x1:x2:x3:x4 where x1:x2:x3:x4 is the IP address + memcpy(addr->addr_bytes + 2, (uint32_t *)&hdr->arp.data.tpa, 4); +} + +#endif /* _ARP_H_ */ diff --git a/VNFs/DPPD-PROX/bng_pkts.h b/VNFs/DPPD-PROX/bng_pkts.h new file mode 100644 index 00000000..82e6199c --- /dev/null +++ b/VNFs/DPPD-PROX/bng_pkts.h @@ -0,0 +1,114 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _BNG_PKTS_H_ +#define _BNG_PKTS_H_ + +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_byteorder.h> + +#include "gre.h" +#include "mpls.h" +#include "qinq.h" +#include "arp.h" +#include "hash_entry_types.h" + +struct cpe_pkt { +#ifdef USE_QINQ + struct qinq_hdr qinq_hdr; +#else + struct ether_hdr ether_hdr; +#endif + struct ipv4_hdr ipv4_hdr; + struct udp_hdr udp_hdr; +} __attribute__((packed)); + +struct cpe_packet_arp { + struct qinq_hdr qinq_hdr; + struct my_arp_t arp; +} __attribute__((packed)); + +/* Struct used for setting all the values a packet + going to the core netwerk. Payload may follow + after the headers, but no need to touch that. */ +struct core_net_pkt_m { + struct ether_hdr ether_hdr; +#ifdef MPLS_ROUTING + union { + struct mpls_hdr mpls; + uint32_t mpls_bytes; + }; +#endif + struct ipv4_hdr tunnel_ip_hdr; + struct gre_hdr gre_hdr; + struct ipv4_hdr ip_hdr; + struct udp_hdr udp_hdr; +} __attribute__((packed)); + +struct core_net_pkt { + struct ether_hdr ether_hdr; + struct ipv4_hdr tunnel_ip_hdr; + struct gre_hdr gre_hdr; + struct ipv4_hdr ip_hdr; + struct udp_hdr udp_hdr; +} __attribute__((packed)); + +#define UPSTREAM_DELTA ((uint32_t)(sizeof(struct core_net_pkt) - sizeof(struct cpe_pkt))) +#define DOWNSTREAM_DELTA ((uint32_t)(sizeof(struct core_net_pkt_m) - sizeof(struct cpe_pkt))) + +struct cpe_pkt_delta { + uint8_t encap[DOWNSTREAM_DELTA]; + struct cpe_pkt pkt; +} __attribute__((packed)); + +static inline void extract_key_cpe(struct rte_mbuf *mbuf, uint64_t* key) +{ + uint8_t* packet = rte_pktmbuf_mtod(mbuf, uint8_t*); +#ifdef USE_QINQ + *key = (*(uint64_t *)(packet + 12)) & 0xFF0FFFFFFF0FFFFF; +#else + *key = rte_bswap32(*(uint32_t *)(packet + 26)) & 0x00FFFFFF; +#endif +} + +static inline void key_core(struct gre_hdr* gre, __attribute__((unused)) struct ipv4_hdr* ip, uint64_t* key) +{ + struct cpe_key *cpe_key = (struct cpe_key*)key; + + cpe_key->gre_id = rte_be_to_cpu_32(gre->gre_id) & 0xFFFFFFF; + +#ifdef USE_QINQ + cpe_key->ip = ip->dst_addr; +#else + cpe_key->ip = 0; +#endif +} + +static inline void extract_key_core(struct rte_mbuf *mbuf, uint64_t* key) +{ + struct core_net_pkt *packet = rte_pktmbuf_mtod(mbuf, struct core_net_pkt *); + key_core(&packet->gre_hdr, &packet->ip_hdr, key); +} + +static inline void extract_key_core_m(struct rte_mbuf *mbuf, uint64_t* key) +{ + struct core_net_pkt_m *packet = rte_pktmbuf_mtod(mbuf, struct core_net_pkt_m *); + key_core(&packet->gre_hdr, &packet->ip_hdr, key); +} + +#endif /* _BNG_PKTS_H_ */ diff --git a/VNFs/DPPD-PROX/cdf.c b/VNFs/DPPD-PROX/cdf.c new file mode 100644 index 00000000..1de40314 --- /dev/null +++ b/VNFs/DPPD-PROX/cdf.c @@ -0,0 +1,148 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdlib.h> +#include <inttypes.h> + +#include <rte_cycles.h> + +#include "prox_malloc.h" +#include "cdf.h" + +static uint32_t round_pow2(uint32_t val) +{ + uint32_t ret; + uint32_t s = 1 << 31; + + while ((s & val) == 0) + s = s >> 1; + if (s == 1U << 31 && s != val) + return 0; + + ret = val; + if (s != ret) + ret = (s << 1); + + return ret; +} + +static uint32_t get_r_max(struct cdf *cdf, uint32_t cur) +{ + uint32_t right_child = cur; + + do { + cur = right_child; + right_child = cur * 2 + 1; + } while (right_child < cdf->elems[0]); + + return cdf->elems[cur]; +} + +struct cdf *cdf_create(uint32_t n_vals, int socket_id) +{ + struct cdf *ret; + size_t mem_size = 0; + uint32_t n_vals_round = round_pow2(n_vals); + + if (0 == n_vals_round) + return NULL; + + mem_size += sizeof(struct cdf); + mem_size += sizeof(((struct cdf *)(0))->elems[0]) * n_vals_round * 2; + ret = prox_zmalloc(mem_size, socket_id); + ret->elems[0] = n_vals; + + /* leafs are [n_vals, 2 * n_vals[. During cdf_add() and + cdf_setup(), rand_max refers to the index of the next leaf + to be added. */ + ret->rand_max = n_vals_round; + ret->first_child = n_vals_round; + ret->seed = rte_rdtsc(); + + return ret; +} + +void cdf_add(struct cdf *cdf, uint32_t len) +{ + cdf->elems[cdf->rand_max++] = len; +} + +int cdf_setup(struct cdf *cdf) +{ + uint32_t last_leaf, first_leaf; + uint32_t first_parent, last_parent; + uint32_t total, multiplier, cur, end; + + if (cdf->elems[0] == 1) { + cdf->rand_max = RAND_MAX; + cdf->elems[1] = RAND_MAX; + cdf->elems[0] = 2; + return 0; + } + + last_leaf = cdf->rand_max; + first_leaf = round_pow2(cdf->elems[0]); + /* Failed to add all elements through cdf_add() */ + if (last_leaf - first_leaf != cdf->elems[0]) + return -1; + + total = 0; + for (uint32_t i = first_leaf; i < last_leaf; ++i) { + total += cdf->elems[i]; + } + + multiplier = RAND_MAX / total; + if (multiplier * total == RAND_MAX) + multiplier--; + cdf->rand_max = multiplier * total; + total = 0; + for (uint32_t i = first_leaf; i < last_leaf; ++i) { + uint32_t cur = cdf->elems[i]; + + /* Each element represents the range between previous + total (non-inclusive) and new total (inclusive). */ + total += cur * multiplier - 1; + cdf->elems[i] = total; + total += 1; + } + end = round_pow2(first_leaf) << 1; + for (uint32_t i = last_leaf; i < end; ++i) { + cdf->elems[i] = RAND_MAX; + } + cdf->first_child = first_leaf; + cdf->elems[0] = end; + + /* Build the binary tree used at run-time. */ + last_leaf = end - 1; + do { + first_parent = first_leaf/2; + last_parent = last_leaf/2; + + for (uint32_t i = first_parent; i <= last_parent; ++i) { + /* The current nodes value should be the + biggest value accessible through its left + child. This value is stored in the right + most child of the left child. The left most + child of the right child is the first value + that can not be accessed through the left + child. */ + cdf->elems[i] = get_r_max(cdf, i * 2); + } + first_leaf = first_parent; + last_leaf = last_parent; + } while (first_parent != last_parent); + return 0; +} diff --git a/VNFs/DPPD-PROX/cdf.h b/VNFs/DPPD-PROX/cdf.h new file mode 100644 index 00000000..821c71bf --- /dev/null +++ b/VNFs/DPPD-PROX/cdf.h @@ -0,0 +1,49 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +struct cdf { + uint32_t rand_max; + uint32_t seed; + uint32_t first_child; + uint32_t elems[0]; +}; + +struct cdf *cdf_create(uint32_t n_vals, int socket_id); +void cdf_add(struct cdf *cdf, uint32_t len); +int cdf_setup(struct cdf *cdf); + +static uint32_t cdf_sample(struct cdf *cdf) +{ + uint32_t left_child, right_child; + uint32_t rand; + + do { + rand = rand_r(&cdf->seed); + } while (rand > cdf->rand_max); + + uint32_t cur = 1; + + while (1) { + left_child = cur * 2; + right_child = cur * 2 + 1; + if (right_child < cdf->elems[0]) + cur = rand > cdf->elems[cur]? right_child : left_child; + else if (left_child < cdf->elems[0]) + cur = left_child; + else + return cur - cdf->first_child; + } +} diff --git a/VNFs/DPPD-PROX/cfgfile.c b/VNFs/DPPD-PROX/cfgfile.c new file mode 100644 index 00000000..80a90937 --- /dev/null +++ b/VNFs/DPPD-PROX/cfgfile.c @@ -0,0 +1,339 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "cfgfile.h" + +#include <rte_string_fns.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> + +#include "parse_utils.h" +#include "log.h" +#include "quit.h" + +#define UINT32_MAX_STR "4294967295" + +/* + * Allocate cfg_file structure. + * Returns pointer to the allocated structure, NULL otherwise. + */ +struct cfg_file *cfg_open(const char *cfg_name) +{ + if (cfg_name == NULL) { + plog_err("\tNo config file name provided\n"); + return NULL; + } + if (access(cfg_name, F_OK)) { + plog_err("\tError opening config file '%s': %s\n", cfg_name, strerror(errno)); + return NULL; + } + + FILE *pf = fopen(cfg_name, "rb"); + if (pf == NULL) { + plog_err("\tError opening config file '%s'\n", cfg_name); + return NULL; + } + + struct cfg_file *pcfg = calloc(1, sizeof(struct cfg_file)); + + if (pcfg == NULL) { + fclose(pf); + plog_err("\tCouldn't allocate memory for config file struct\n"); + return NULL; + } + + pcfg->pfile = pf; + pcfg->name = strdup(cfg_name); + + return pcfg; +} + +/* Free memory allocated for cfg_file structure. + * Returns 0 on success, -1 if the pointer to the pcfg is invalid */ +int cfg_close(struct cfg_file *pcfg) +{ + if (pcfg == NULL) { + return -1; + } + + if (pcfg->name != NULL) { + free(pcfg->name); + } + if (pcfg->err_section != NULL) { + free(pcfg->err_section); + } + if (pcfg->pfile != NULL) { + fclose(pcfg->pfile); + } + + free(pcfg); + return 0; +} + +static int cfg_get_pos(struct cfg_file *pcfg, fpos_t *pos) +{ + pcfg->index_line = pcfg->line; + return fgetpos(pcfg->pfile, pos); +} + +static int cfg_set_pos(struct cfg_file *pcfg, fpos_t *pos) +{ + pcfg->line = pcfg->index_line; + return fsetpos(pcfg->pfile, pos); +} + +/* + * Read a line from the configuration file. + * Returns: on success length of the line read from the file is returned, + * 0 to indicate End of File, + * -1 in case of wrong function parameters + */ +static int cfg_get_line(struct cfg_file *pcfg, char *buffer, unsigned len, int raw_lines) +{ + char *ptr; + + if (pcfg == NULL || pcfg->pfile == NULL || buffer == NULL || len == 0) { + return -1; + } + + do { + ptr = fgets(buffer, len, pcfg->pfile); + if (ptr == NULL) { + return 0; /* end of file */ + } + ++pcfg->line; + + if (raw_lines) { + break; + } + + /* remove comments */ + ptr = strchr(buffer, ';'); + if (ptr != NULL) { + *ptr = '\0'; + } + else { + ptr = strchr(buffer, '\0'); + } + + /* remove trailing spaces */ + if (ptr != buffer) { + ptr--; + while (isspace(*ptr)) { + *ptr = '\0'; + ptr--; + } + } + + ptr = buffer; + /* remove leading spaces */ + while (*ptr && isspace(*ptr)) { + ++ptr; + } + if (ptr != buffer) { + strcpy(buffer, ptr); + ptr = buffer; + } + } + while (*ptr == '\0'); /* skip empty strings */ + + return strlen(buffer); +} + +/* + * Checks if buffer contains section name specified by the cfg_section pointer. + * Returns NULL if section name does not match, cfg_section pointer otherwise + */ +static struct cfg_section *cfg_check_section(char *buffer, struct cfg_section *psec) +{ + char *pend; + unsigned len; + static const char *valid = "0123456789,hs- \t"; + + pend = strchr(buffer, ']'); + if (pend == NULL) { + return NULL; /* ']' not found: invalid section name */ + } + + *pend = '\0'; + + /* check if section is indexed */ + pend = strchr(psec->name, '#'); + if (pend == NULL) { + return (strcmp(buffer, psec->name) == 0) ? psec : NULL; + } + + /* get section index */ + len = pend - psec->name; + if (strncmp(buffer, psec->name, len) != 0) { + return NULL; + } + pend = buffer + len; + if (*pend == '\0') { + return NULL; + } + /* only numeric characters are valid for section index + (currently, variables not checked!) */ + if (pend[0] != '$') { + for (len = 0; pend[len] != '\0'; ++len) { + if (strchr(valid, pend[len]) == NULL) { + return NULL; + } + } + } + + psec->nbindex = parse_list_set(psec->indexp, pend, MAX_INDEX); + PROX_PANIC(psec->nbindex == -1, "\t\tError in cfg_check_section('%s'): %s\n", buffer, get_parse_err()); + + for (int i = 0; i < psec->nbindex; ++i) { + psec->indexp[i] |= CFG_INDEXED; + } + + return psec; +} + +static char *cfg_get_section_name(struct cfg_section *psec) +{ + char *name; + + if (!(psec->indexp[0] & CFG_INDEXED)) { + return strdup(psec->name); + } + + name = malloc(strlen(psec->name) + strlen(UINT32_MAX_STR)); + if (name != NULL) { + strcpy(name, psec->name); + char *pidx = strchr(name, '#'); + if (pidx != NULL) { + sprintf(pidx, "%u", psec->indexp[0] & ~CFG_INDEXED); + } + } + return name; +} + +/* + * Reads configuration file and parses section specified by psec pointer. + * Returns 0 on success, -1 otherwise + */ +int cfg_parse(struct cfg_file *pcfg, struct cfg_section *psec) +{ + int error; + unsigned entry = 0; + fpos_t pos; + int index_count = 0; + struct cfg_section *section = NULL; + char buffer[sizeof(pcfg->cur_line)] = {0}; + + if (pcfg == NULL || psec == NULL) { + return -1; + } + + pcfg->line = 0; + fseek(pcfg->pfile, 0, SEEK_SET); + + /* read configuration file and parse section specified by psec pointer */ + while (1) { + if (psec->raw_lines) { + /* skip until section starts */ + char *lines = pcfg->cur_line; + size_t max_len = sizeof(pcfg->cur_line); + char *ret; + + do { + ret = fgets(lines, max_len, pcfg->pfile); + if (ret && *ret == '[') { + section = cfg_check_section(lines + 1, psec); + } + } while (!section && ret); + + if (!ret) + return 0; + + do { + + ret = fgets(buffer, sizeof(buffer), pcfg->pfile); + if (ret && *ret != '[') { + size_t l = strlen(buffer); + strncpy(lines, buffer, max_len); + max_len -= l; + lines += l; + } + } while ((ret && *ret != '[')); + + if (section != NULL) { + error = section->parser(section->indexp[index_count], pcfg->cur_line, section->data); + if (error != 0) { + section->error = error; + /* log only the very first error */ + if (!pcfg->err_section) { + pcfg->err_line = pcfg->line; + pcfg->err_entry = entry; + pcfg->err_section = cfg_get_section_name(section); + } + return 0; + } + ++entry; + } + return 0; + } + + while (cfg_get_line(pcfg, buffer, MAX_CFG_STRING_LEN, psec->raw_lines) > 0) { + strncpy(pcfg->cur_line, buffer, sizeof(pcfg->cur_line)); + if (*buffer == '[') { + if (index_count + 1 < psec->nbindex) { + // Need to loop - go back to recorded postion in file + cfg_set_pos(pcfg, &pos); + ++index_count; + continue; + } + else { + section = cfg_check_section(buffer + 1, psec); + entry = 0; + index_count = 0; + cfg_get_pos(pcfg, &pos); + continue; + } + } + /* call parser procedure for each line in the section */ + if (section != NULL) { + error = section->parser(section->indexp[index_count], buffer, section->data); + if (error != 0) { + section->error = error; + /* log only the very first error */ + if (!pcfg->err_section) { + pcfg->err_line = pcfg->line; + pcfg->err_entry = entry; + pcfg->err_section = cfg_get_section_name(section); + } + return 0; + } + ++entry; + } + } + if (index_count + 1 < psec->nbindex) { + // Last core config contained multiple cores - loop back + cfg_set_pos(pcfg, &pos); + ++index_count; + } + else { + break; + } + } + return 0; +} diff --git a/VNFs/DPPD-PROX/cfgfile.h b/VNFs/DPPD-PROX/cfgfile.h new file mode 100644 index 00000000..41b474ee --- /dev/null +++ b/VNFs/DPPD-PROX/cfgfile.h @@ -0,0 +1,60 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CFG_FILE_H_ +#define _CFG_FILE_H_ + +#include <stdio.h> + +#define DEFAULT_CONFIG_FILE "./prox.cfg" + +/* configuration file line parser procedure */ +typedef int (*cfg_parser)(unsigned sindex, char *str, void *data); + +#define CFG_INDEXED 0x80000000 /* section contains index [name #] */ +#define MAX_INDEX 64 + +struct cfg_section { + const char *name; /* section name without [] */ + cfg_parser parser; /* section parser function */ + void *data; /* data to be passed to the parser */ + /* set by parsing procedure */ + unsigned indexp[MAX_INDEX]; + int raw_lines; /* if set, do not remove text after ';' */ + int nbindex; + int error; +}; + +#define MAX_CFG_STRING_LEN 8192 +#define STRING_TERMINATOR_LEN 4 + +struct cfg_file { + char *name; + FILE *pfile; + unsigned line; + unsigned index_line; + /* set in case of any error */ + unsigned err_line; + char *err_section; + unsigned err_entry; + char cur_line[MAX_CFG_STRING_LEN + STRING_TERMINATOR_LEN]; +}; + +struct cfg_file *cfg_open(const char *cfg_name); +int cfg_parse(struct cfg_file *pcfg, struct cfg_section *psec); +int cfg_close(struct cfg_file *pcfg); + +#endif /* _CFGFILE_H_ */ diff --git a/VNFs/DPPD-PROX/clock.c b/VNFs/DPPD-PROX/clock.c new file mode 100644 index 00000000..6e057101 --- /dev/null +++ b/VNFs/DPPD-PROX/clock.c @@ -0,0 +1,261 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "clock.h" + +#include <stdio.h> +#include <string.h> + +#include <rte_cycles.h> + +/* Calibrate TSC overhead by reading NB_READ times and take the smallest value. + Bigger values are caused by external influence and can be discarded. The best + estimate is the smallest read value. */ +#define NB_READ 10000 + +uint32_t rdtsc_overhead; +uint32_t rdtsc_overhead_stats; + +uint64_t thresh; +uint64_t tsc_hz; + +/* calculate how much overhead is involved with calling rdtsc. This value has + to be taken into account where the time spent running a small piece of code + is measured */ +static void init_tsc_overhead(void) +{ + volatile uint32_t min_without_overhead = UINT32_MAX; + volatile uint32_t min_with_overhead = UINT32_MAX; + volatile uint32_t min_stats_overhead = UINT32_MAX; + volatile uint64_t start1, end1; + volatile uint64_t start2, end2; + + for (uint32_t i = 0; i < NB_READ; ++i) { + start1 = rte_rdtsc(); + end1 = rte_rdtsc(); + + start2 = rte_rdtsc(); + end2 = rte_rdtsc(); + end2 = rte_rdtsc(); + + if (min_without_overhead > end1 - start1) { + min_without_overhead = end1 - start1; + } + + if (min_with_overhead > end2 - start2) { + min_with_overhead = end2 - start2; + } + } + + rdtsc_overhead = min_with_overhead - min_without_overhead; + + start1 = rte_rdtsc(); + end1 = rte_rdtsc(); + /* forbid the compiler to optimize this dummy variable */ + volatile int dummy = 0; + for (uint32_t i = 0; i < NB_READ; ++i) { + start1 = rte_rdtsc(); + dummy += 32; + end1 = rte_rdtsc(); + + if (min_stats_overhead > end2 - start2) { + min_stats_overhead = end1 - start1; + } + } + + rdtsc_overhead_stats = rdtsc_overhead + min_stats_overhead - min_without_overhead; +} + +void clock_init(void) +{ + init_tsc_overhead(); + tsc_hz = rte_get_tsc_hz(); + thresh = UINT64_MAX/tsc_hz; +} + +uint64_t str_to_tsc(const char *from) +{ + const uint64_t hz = rte_get_tsc_hz(); + uint64_t ret; + char str[16]; + + strncpy(str, from, sizeof(str)); + + char *frac = strchr(str, '.'); + + if (frac) { + *frac = 0; + frac++; + } + + ret = hz * atoi(str); + + if (!frac) + return ret; + + uint64_t nsec = 0; + uint64_t multiplier = 100000000; + + for (size_t i = 0; i < strlen(frac); ++i) { + nsec += (frac[i] - '0') * multiplier; + multiplier /= 10; + } + + /* Wont overflow until CPU freq is ~18.44 GHz */ + ret += hz * nsec/1000000000; + + return ret; +} + +uint64_t sec_to_tsc(uint64_t sec) +{ + if (sec < UINT64_MAX/rte_get_tsc_hz()) + return sec * rte_get_tsc_hz(); + else + return UINT64_MAX; +} + +uint64_t msec_to_tsc(uint64_t msec) +{ + if (msec < UINT64_MAX/rte_get_tsc_hz()) + return msec * rte_get_tsc_hz() / 1000; + else + return msec / 1000 * rte_get_tsc_hz(); +} + +uint64_t usec_to_tsc(uint64_t usec) +{ + if (usec < UINT64_MAX/rte_get_tsc_hz()) + return usec * rte_get_tsc_hz() / 1000000; + else + return usec / 1000000 * rte_get_tsc_hz(); +} + +uint64_t nsec_to_tsc(uint64_t nsec) +{ + if (nsec < UINT64_MAX/rte_get_tsc_hz()) + return nsec * rte_get_tsc_hz() / 1000000000; + else + return nsec / 1000000000 * rte_get_tsc_hz(); +} + +uint64_t tsc_to_msec(uint64_t tsc) +{ + if (tsc < UINT64_MAX / 1000) { + return tsc * 1000 / rte_get_tsc_hz(); + } else { + return tsc / (rte_get_tsc_hz() / 1000); + } +} + +uint64_t tsc_to_usec(uint64_t tsc) +{ + if (tsc < UINT64_MAX / 1000000) { + return tsc * 1000000 / rte_get_tsc_hz(); + } else { + return tsc / (rte_get_tsc_hz() / 1000000); + } +} + +uint64_t tsc_to_nsec(uint64_t tsc) +{ + if (tsc < UINT64_MAX / 1000000000) { + return tsc * 1000000000 / rte_get_tsc_hz(); + } else { + return tsc / (rte_get_tsc_hz() / 1000000000); + } +} + +uint64_t tsc_to_sec(uint64_t tsc) +{ + return tsc / rte_get_tsc_hz(); +} + +struct time_unit tsc_to_time_unit(uint64_t tsc) +{ + struct time_unit ret; + uint64_t hz = rte_get_tsc_hz(); + + ret.sec = tsc/hz; + ret.nsec = (tsc - ret.sec*hz)*1000000000/hz; + + return ret; +} + +uint64_t time_unit_to_usec(struct time_unit *time_unit) +{ + return time_unit->sec * 1000000 + time_unit->nsec/1000; +} + +uint64_t time_unit_to_nsec(struct time_unit *time_unit) +{ + return time_unit->sec * 1000000000 + time_unit->nsec; +} + +int time_unit_cmp(struct time_unit *left, struct time_unit *right) +{ + if (left->sec < right->sec) + return -1; + if (left->sec > right->sec) + return 1; + + if (left->nsec < right->nsec) + return -1; + if (left->nsec > right->nsec) + return -1; + return 0; +} + +uint64_t freq_to_tsc(uint64_t times_per_sec) +{ + return rte_get_tsc_hz()/times_per_sec; +} + +void tsc_to_tv(struct timeval *tv, const uint64_t tsc) +{ + uint64_t hz = rte_get_tsc_hz(); + uint64_t sec = tsc/hz; + + tv->tv_sec = sec; + tv->tv_usec = ((tsc - sec * hz) * 1000000) / hz; +} + +void tv_to_tsc(const struct timeval *tv, uint64_t *tsc) +{ + uint64_t hz = rte_get_tsc_hz(); + *tsc = tv->tv_sec * hz; + *tsc += tv->tv_usec * hz / 1000000; +} + +struct timeval tv_diff(const struct timeval *cur, const struct timeval *next) +{ + uint64_t sec, usec; + + sec = next->tv_sec - cur->tv_sec; + if (next->tv_usec < cur->tv_usec) { + usec = next->tv_usec + 1000000 - cur->tv_usec; + sec -= 1; + } + else + usec = next->tv_usec - cur->tv_usec; + + struct timeval ret = { + .tv_sec = sec, + .tv_usec = usec, + }; + + return ret; +} diff --git a/VNFs/DPPD-PROX/clock.h b/VNFs/DPPD-PROX/clock.h new file mode 100644 index 00000000..719968ab --- /dev/null +++ b/VNFs/DPPD-PROX/clock.h @@ -0,0 +1,76 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CLOCK_H_ +#define _CLOCK_H_ + +#include <inttypes.h> + +extern uint32_t rdtsc_overhead; +extern uint32_t rdtsc_overhead_stats; + +void clock_init(void); + +struct time_unit { + uint64_t sec; + uint64_t nsec; +}; + +struct time_unit_err { + struct time_unit time; + struct time_unit error; +}; + +extern uint64_t thresh; +extern uint64_t tsc_hz; + +static uint64_t val_to_rate(uint64_t val, uint64_t delta_t) +{ + if (val < thresh) { + return val * tsc_hz / delta_t; + } else if (val >> 2 < thresh) { + /* bytes per sec malls into this category ... */ + return ((val >> 2) * tsc_hz) / (delta_t >> 2); + } else { + if (delta_t < tsc_hz) + return UINT64_MAX; + else + return val / (delta_t/tsc_hz); + } +} + +/* The precision of the conversion is nano-second. */ +uint64_t str_to_tsc(const char *from); +uint64_t sec_to_tsc(uint64_t sec); +uint64_t msec_to_tsc(uint64_t msec); +uint64_t usec_to_tsc(uint64_t usec); +uint64_t nsec_to_tsc(uint64_t nsec); +uint64_t freq_to_tsc(uint64_t times_per_sec); +uint64_t tsc_to_msec(uint64_t tsc); +uint64_t tsc_to_usec(uint64_t tsc); +uint64_t tsc_to_nsec(uint64_t tsc); +uint64_t tsc_to_sec(uint64_t tsc); +struct time_unit tsc_to_time_unit(uint64_t tsc); +uint64_t time_unit_to_usec(struct time_unit *time_unit); +uint64_t time_unit_to_nsec(struct time_unit *time_unit); +int time_unit_cmp(struct time_unit *left, struct time_unit *right); + +struct timeval; +void tsc_to_tv(struct timeval *tv, const uint64_t tsc); +void tv_to_tsc(const struct timeval *tv, uint64_t *tsc); +struct timeval tv_diff(const struct timeval *tv1, const struct timeval * tv2); + +#endif /* _CLOCK_H_ */ diff --git a/VNFs/DPPD-PROX/cmd_parser.c b/VNFs/DPPD-PROX/cmd_parser.c new file mode 100644 index 00000000..95688477 --- /dev/null +++ b/VNFs/DPPD-PROX/cmd_parser.c @@ -0,0 +1,2031 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "input.h" +#include "cmd_parser.h" +#include "commands.h" +#include "run.h" +#include "display.h" +#include "log.h" +#include "prox_cfg.h" +#include "prox_port_cfg.h" +#include "task_base.h" +#include "lconf.h" +#include "main.h" +#include "parse_utils.h" +#include "stats_parser.h" +#include "stats_port.h" +#include "stats_latency.h" +#include "stats_global.h" +#include "stats_prio_task.h" + +#include "handle_routing.h" +#include "handle_qinq_decap4.h" +#include "handle_lat.h" +#include "handle_arp.h" +#include "handle_gen.h" +#include "handle_acl.h" +#include "handle_irq.h" +#include "defines.h" +#include "prox_cfg.h" +#include "version.h" +#include "stats_latency.h" +#include "handle_cgnat.h" +#include "handle_impair.h" + +static int core_task_is_valid(int lcore_id, int task_id) +{ + if (lcore_id >= RTE_MAX_LCORE) { + plog_err("Invalid core id %u (lcore ID above %d)\n", lcore_id, RTE_MAX_LCORE); + return 0; + } + else if (!prox_core_active(lcore_id, 0)) { + plog_err("Invalid core id %u (lcore is not active)\n", lcore_id); + return 0; + } + else if (task_id >= lcore_cfg[lcore_id].n_tasks_all) { + plog_err("Invalid task id (valid task IDs for core %u are below %u)\n", + lcore_id, lcore_cfg[lcore_id].n_tasks_all); + return 0; + } + return 1; +} + +static int cores_task_are_valid(unsigned int *lcores, int task_id, unsigned int nb_cores) +{ + unsigned int lcore_id; + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (lcore_id >= RTE_MAX_LCORE) { + plog_err("Invalid core id %u (lcore ID above %d)\n", lcore_id, RTE_MAX_LCORE); + return 0; + } + else if (!prox_core_active(lcore_id, 0)) { + plog_err("Invalid core id %u (lcore is not active)\n", lcore_id); + return 0; + } + else if (task_id >= lcore_cfg[lcore_id].n_tasks_all) { + plog_err("Invalid task id (valid task IDs for core %u are below %u)\n", + lcore_id, lcore_cfg[lcore_id].n_tasks_all); + return 0; + } + } + return 1; +} + +static int parse_core_task(const char *str, uint32_t *lcore_id, uint32_t *task_id, unsigned int *nb_cores) +{ + char str_lcore_id[128]; + int ret; + + if (2 != sscanf(str, "%s %u", str_lcore_id, task_id)) + return -1; + + if ((ret = parse_list_set(lcore_id, str_lcore_id, RTE_MAX_LCORE)) <= 0) { + plog_err("Invalid core while parsing command (%s)\n", get_parse_err()); + return -1; + } + *nb_cores = ret; + + return 0; +} + +static const char *strchr_skip_twice(const char *str, int chr) +{ + str = strchr(str, chr); + if (!str) + return NULL; + str = str + 1; + + str = strchr(str, chr); + if (!str) + return NULL; + return str + 1; +} + +static int parse_cmd_quit(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + quit(); + return 0; +} + +static int parse_cmd_quit_force(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + abort(); +} + +static int parse_cmd_history(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + if (input->history) { + input->history(input); + return 0; + } + plog_err("Invalid history comand "); + return -1; +} + +static int parse_cmd_echo(const char *str, struct input *input) +{ + if (strcmp(str, "") == 0) { + return -1; + } + + char resolved[2048]; + + if (parse_vars(resolved, sizeof(resolved), str)) { + return 0; + } + + if (input->reply) { + if (strlen(resolved) + 2 < sizeof(resolved)) { + resolved[strlen(resolved) + 1] = 0; + resolved[strlen(resolved)] = '\n'; + } + else + return 0; + + input->reply(input, resolved, strlen(resolved)); + } else + plog_info("%s\n", resolved); + + return 0; +} + +static int parse_cmd_reset_stats(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + stats_reset(); + return 0; +} + +static int parse_cmd_reset_lat_stats(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + stats_latency_reset(); + return 0; +} + +static int parse_cmd_trace(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], task_id, nb_packets, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%u", &nb_packets) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + cmd_trace(lcores[i], task_id, nb_packets); + } + } + return 0; +} + +static int parse_cmd_dump_rx(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], task_id, nb_packets, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%u", &nb_packets) != 1) { + return -1; + } + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + cmd_dump(lcores[i], task_id, nb_packets, input, 1, 0); + } + } + return 0; +} + +static int parse_cmd_pps_unit(const char *str, struct input *input) +{ + uint32_t val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + display_set_pps_unit(val); + return 0; +} + +static int parse_cmd_dump_tx(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], task_id, nb_packets, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%u", &nb_packets) != 1) { + return -1; + } + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + cmd_dump(lcores[i], task_id, nb_packets, input, 0, 1); + } + } + return 0; +} + +static int parse_cmd_rate(const char *str, struct input *input) +{ + unsigned queue, port, rate; + + if (sscanf(str, "%u %u %u", &queue, &port, &rate) != 3) { + return -1; + } + + if (port > PROX_MAX_PORTS) { + plog_err("Max port id allowed is %u (specified %u)\n", PROX_MAX_PORTS, port); + } + else if (!prox_port_cfg[port].active) { + plog_err("Port %u not active\n", port); + } + else if (queue >= prox_port_cfg[port].n_txq) { + plog_err("Number of active queues is %u\n", + prox_port_cfg[port].n_txq); + } + else if (rate > prox_port_cfg[port].link_speed) { + plog_err("Max rate allowed on port %u queue %u is %u Mbps\n", + port, queue, prox_port_cfg[port].link_speed); + } + else { + if (rate == 0) { + plog_info("Disabling rate limiting on port %u queue %u\n", + port, queue); + } + else { + plog_info("Setting rate limiting to %u Mbps on port %u queue %u\n", + rate, port, queue); + } + rte_eth_set_queue_rate_limit(port, queue, rate); + } + return 0; +} + +int task_is_mode(uint32_t lcore_id, uint32_t task_id, const char *mode, const char *sub_mode) +{ + struct task_init *t = lcore_cfg[lcore_id].targs[task_id].task_init; + + return !strcmp(t->mode_str, mode) && !strcmp(t->sub_mode_str, sub_mode); +} + +int task_is_sub_mode(uint32_t lcore_id, uint32_t task_id, const char *sub_mode) +{ + struct task_init *t = lcore_cfg[lcore_id].targs[task_id].task_init; + + return !strcmp(t->sub_mode_str, sub_mode); +} + +static void log_pkt_count(uint32_t count, uint32_t lcore_id, uint32_t task_id) +{ + if (count == UINT32_MAX) + plog_info("Core %u task %u will keep sending packets\n", lcore_id, task_id); + else if (count == 0) + plog_info("Core %u task %u waits for next count command\n", lcore_id, task_id); + else + plog_info("Core %u task %u stopping after %u packets\n", lcore_id, task_id, count); +} + +static int parse_cmd_count(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, count, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%u", &count) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else { + struct task_base *task = lcore_cfg[lcore_id].tasks_all[task_id]; + + log_pkt_count(count, lcore_id, task_id); + task_gen_set_pkt_count(task, count); + } + } + } + return 0; +} + +static int parse_cmd_set_probability(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + float probability; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%f", &probability) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "impair", "")) { + plog_err("Core %u task %u is not impairing packets\n", lcore_id, task_id); + } + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + task_impair_set_proba(tbase, probability); + } + } + return 0; +} + +static int parse_cmd_delay_us(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, delay_us, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%d", &delay_us) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "impair", "")) { + plog_err("Core %u task %u is not impairing packets\n", lcore_id, task_id); + } + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + task_impair_set_delay_us(tbase, delay_us, 0); + } + } + return 0; +} + +static int parse_cmd_random_delay_us(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, delay_us, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%d", &delay_us) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "impair", "")) { + plog_err("Core %u task %u is not impairing packets\n", lcore_id, task_id); + } + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + task_impair_set_delay_us(tbase, 0, delay_us); + } + } + return 0; +} + +static int parse_cmd_bypass(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, pkt_size, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if ((prox_cfg.flags & DSF_ENABLE_BYPASS) == 0) { + plog_err("enable bypass not set => command not supported\n"); + return -1; + } + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (bypass_task(lcore_id, task_id) != 0) + return -1; + } + } + return 0; +} + +static int parse_cmd_reconnect(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, pkt_size, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (reconnect_task(lcore_id, task_id) != 0) + return -1; + } + } + return 0; +} + +static int parse_cmd_pkt_size(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, pkt_size, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%d", &pkt_size) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + if (task_gen_set_pkt_size(tbase, pkt_size) != 0) + return -1; + } + } + return 0; +} + +static int parse_cmd_speed(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], task_id, lcore_id, nb_cores; + float speed; + unsigned i; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%f", &speed) != 1) { + return -1; + } + + if (!cores_task_are_valid(lcores, task_id, nb_cores)) { + return 0; + } + + for (i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else if (speed > 400.0f || speed < 0.0f) { + plog_err("Speed out of range (must be betweeen 0%% and 100%%)\n"); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + uint64_t bps = speed * 12500000; + + plog_info("Setting rate to %"PRIu64" Bps\n", bps); + + task_gen_set_rate(tbase, bps); + } + } + return 0; +} + +static int parse_cmd_speed_byte(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + uint64_t bps; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%"PRIu64"", &bps) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else if (bps > 1250000000) { + plog_err("Speed out of range (must be <= 1250000000)\n"); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + plog_info("Setting rate to %"PRIu64" Bps\n", bps); + task_gen_set_rate(tbase, bps); + } + } + } + return 0; +} + +static int parse_cmd_reset_randoms_all(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + unsigned task_id, lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + for (task_id = 0; task_id < lcore_cfg[lcore_id].n_tasks_all; task_id++) { + if ((task_is_mode(lcore_id, task_id, "gen", "")) || (task_is_mode(lcore_id, task_id, "gen", "l3"))) { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + uint32_t n_rands = task_gen_get_n_randoms(tbase); + + plog_info("Resetting randoms on core %d task %d from %d randoms\n", lcore_id, task_id, n_rands); + task_gen_reset_randoms(tbase); + } + } + } + return 0; +} + +static int parse_cmd_reset_values_all(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + unsigned task_id, lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + for (task_id = 0; task_id < lcore_cfg[lcore_id].n_tasks_all; task_id++) { + if ((task_is_mode(lcore_id, task_id, "gen", "")) || (task_is_mode(lcore_id, task_id, "gen", "l3"))) { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + plog_info("Resetting values on core %d task %d\n", lcore_id, task_id); + task_gen_reset_values(tbase); + } + } + } + return 0; +} + +static int parse_cmd_reset_values(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + plog_info("Resetting values on core %d task %d\n", lcore_id, task_id); + task_gen_reset_values(tbase); + } + } + } + return 0; +} + +static int parse_cmd_set_value(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, value, nb_cores; + unsigned short offset; + uint8_t value_len; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%hu %u %hhu", &offset, &value, &value_len) != 3) { + return -1; + } + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else if (offset > ETHER_MAX_LEN) { + plog_err("Offset out of range (must be less then %u)\n", ETHER_MAX_LEN); + } + else if (value_len > 4) { + plog_err("Length out of range (must be less then 4)\n"); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + if (task_gen_set_value(tbase, value, offset, value_len)) + plog_info("Unable to set Byte %"PRIu16" to %"PRIu8" - too many value set\n", offset, value); + else + plog_info("Setting Byte %"PRIu16" to %"PRIu32"\n", offset, value); + } + } + } + return 0; +} + +static int parse_cmd_set_random(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + unsigned short offset; + uint8_t value_len; + char rand_str[64]; + int16_t rand_id = -1; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%hu %32s %hhu", &offset, rand_str, &value_len) != 3) { + return -1; + } + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else if (offset > ETHER_MAX_LEN) { + plog_err("Offset out of range (must be less then %u)\n", ETHER_MAX_LEN); + } + else if (value_len > 4) { + plog_err("Length out of range (must be less then 4)\n"); + } else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + if (task_gen_add_rand(tbase, rand_str, offset, rand_id)) { + plog_warn("Random not added on core %u task %u\n", lcore_id, task_id); + } + } + } + } + return 0; +} + +static int parse_cmd_thread_info(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + for (unsigned int i = 0; i < nb_cores; i++) { + cmd_thread_info(lcores[i], task_id); + } + return 0; +} + +static int parse_cmd_verbose(const char *str, struct input *input) +{ + unsigned id; + + if (sscanf(str, "%u", &id) != 1) { + return -1; + } + + if (plog_set_lvl(id) != 0) { + plog_err("Cannot set log level to %u\n", id); + } + return 0; +} + +static int parse_cmd_arp_add(const char *str, struct input *input) +{ + struct arp_msg amsg; + struct arp_msg *pmsg = &amsg; + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + struct rte_ring *ring; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (strcmp(str, "")) + return -1; + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + if (str_to_arp_msg(&amsg, str) == 0) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + ring = ctrl_rings[lcore_id*MAX_TASKS_PER_CORE + task_id]; + if (!ring) { + plog_err("No ring for control messages to core %u task %u\n", lcore_id, task_id); + } + else { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&pmsg, 1)); +#else + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&pmsg, 1, NULL) == 0); +#endif + while (!rte_ring_empty(ring)); + } + } + return 0; + } + } + return -1; +} + +static int parse_cmd_rule_add(const char *str, struct input *input) +{ + struct rte_ring *ring; + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (strcmp(str, "")) + return -1; + char *fields[9]; + char str_cpy[255]; + strncpy(str_cpy, str, 255); + // example add rule command: rule add 15 0 1&0x0fff 1&0x0fff 0&0 128.0.0.0/1 128.0.0.0/1 5000-5000 5000-5000 allow + int ret = rte_strsplit(str_cpy, 255, fields, 9, ' '); + if (ret != 8) { + return -1; + } + + struct acl4_rule rule; + struct acl4_rule *prule = &rule; + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + if (str_to_rule(&rule, fields, -1, 1) == 0) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + ring = ctrl_rings[lcore_id*MAX_TASKS_PER_CORE + task_id]; + if (!ring) { + plog_err("No ring for control messages to core %u task %u\n", lcore_id, task_id); + } + else { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&prule, 1)); +#else + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&prule, 1, NULL) == 0); +#endif + while (!rte_ring_empty(ring)); + } + } + return 0; + } + } + return -1; +} + +static int parse_cmd_gateway_ip(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, ip[4], nb_cores, i; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (!strcmp(str, "")) + return -1; + if (sscanf(str, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4) { + return -1; + } + for (i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if ((!task_is_mode(lcore_id, task_id, "gen", "")) && (!task_is_mode(lcore_id, task_id, "gen", "l3"))) { + plog_err("Core %u task %u is not generating packets\n", lcore_id, task_id); + } + else { + uint32_t gateway_ip = ((ip[3] & 0xFF) << 24) | ((ip[2] & 0xFF) << 16) | ((ip[1] & 0xFF) << 8) | ((ip[0] & 0xFF) << 0); + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + plog_info("Setting gateway ip to %s\n", str); + task_gen_set_gateway_ip(tbase, gateway_ip); + } + } + return 0; +} + +static int parse_cmd_local_ip(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, ip[4], nb_cores, i; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (!strcmp(str, "")) + return -1; + if (sscanf(str, "%u.%u.%u.%u", ip, ip + 1, ip + 2, ip + 3) != 4) { + return -1; + } + for (i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "arp", "local")) { + plog_err("Core %u task %u is not in arp mode\n", lcore_id, task_id); + } + else { + uint32_t local_ip = ((ip[3] & 0xFF) << 24) | ((ip[2] & 0xFF) << 16) | ((ip[1] & 0xFF) << 8) | ((ip[0] & 0xFF) << 0); + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + plog_info("Setting local ip to %s\n", str); + task_arp_set_local_ip(tbase, local_ip); + } + } + return 0; +} + +static int parse_cmd_route_add(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, prefix, next_hop_idx, ip[4], nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (strcmp(str, "")) + return -1; + if (sscanf(str, "%u.%u.%u.%u/%u %u", ip, ip + 1, ip + 2, ip + 3, + &prefix, &next_hop_idx) != 8) { + return -1; + } + struct rte_ring *ring; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + ring = ctrl_rings[lcore_id*MAX_TASKS_PER_CORE + task_id]; + if (!ring) { + plog_err("No ring for control messages to core %u task %u\n", lcore_id, task_id); + } + else { + struct route_msg rmsg; + struct route_msg *pmsg = &rmsg; + + rmsg.ip_bytes[0] = ip[0]; + rmsg.ip_bytes[1] = ip[1]; + rmsg.ip_bytes[2] = ip[2]; + rmsg.ip_bytes[3] = ip[3]; + rmsg.prefix = prefix; + rmsg.nh = next_hop_idx; +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&pmsg, 1)); +#else + while (rte_ring_sp_enqueue_bulk(ring, (void *const *)&pmsg, 1, NULL) == 0); +#endif + while (!rte_ring_empty(ring)); + } + } + } + return 0; +} + +static int parse_cmd_start(const char *str, struct input *input) +{ + int task_id = -1; + + if (strncmp(str, "all", 3) == 0) { + str += 3; + sscanf(str, "%d", &task_id); + + start_core_all(task_id); + req_refresh(); + return 0; + } + + uint32_t cores[64] = {0}; + int ret; + ret = parse_list_set(cores, str, 64); + if (ret < 0) { + return -1; + } + str = strchr(str, ' '); + + if (str) { + sscanf(str, "%d", &task_id); + } + start_cores(cores, ret, task_id); + req_refresh(); + return 0; +} + +static int parse_cmd_stop(const char *str, struct input *input) +{ + int task_id = -1; + + if (strncmp(str, "all", 3) == 0) { + str += 3; + sscanf(str, "%d", &task_id); + stop_core_all(task_id); + req_refresh(); + return 0; + } + + uint32_t cores[64] = {0}; + int ret; + ret = parse_list_set(cores, str, 64); + if (ret < 0) { + return -1; + } + str = strchr(str, ' '); + + if (str) { + sscanf(str, "%d", &task_id); + } + stop_cores(cores, ret, task_id); + req_refresh(); + + return 0; +} + +static int parse_cmd_rx_distr_start(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_rx_distr_start(lcore_id[i]); + return 0; +} + +static int parse_cmd_tx_distr_start(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_tx_distr_start(lcore_id[i]); + return 0; +} + +static int parse_cmd_rx_distr_stop(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_rx_distr_stop(lcore_id[i]); + return 0; +} + +static int parse_cmd_tx_distr_stop(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_tx_distr_stop(lcore_id[i]); + return 0; +} + +static int parse_cmd_rx_distr_reset(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_rx_distr_rst(lcore_id[i]); + return 0; +} + +static int parse_cmd_tx_distr_reset(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_tx_distr_rst(lcore_id[i]); + return 0; +} + +static int parse_cmd_rx_distr_show(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_rx_distr_show(lcore_id[i]); + return 0; +} + +static int parse_cmd_tx_distr_show(const char *str, struct input *input) +{ + unsigned lcore_id[RTE_MAX_LCORE]; + + int nb_cores; + + nb_cores = parse_list_set(lcore_id, str, sizeof(lcore_id)/sizeof(lcore_id[0])); + + if (nb_cores <= 0) { + return -1; + } + + for (int i = 0; i < nb_cores; ++i) + cmd_tx_distr_show(lcore_id[i]); + return 0; +} + +static int parse_cmd_tot_stats(const char *str, struct input *input) +{ + if (strcmp("", str) != 0) { + return -1; + } + + struct global_stats_sample *gsl = stats_get_global_stats(1); + uint64_t tot_rx = gsl->host_rx_packets; + uint64_t tot_tx = gsl->host_tx_packets; + uint64_t last_tsc = gsl->tsc; + + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"\n", + tot_rx, tot_tx, last_tsc, rte_get_tsc_hz()); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("RX: %"PRIu64", TX: %"PRIu64"\n", tot_rx, tot_tx); + } + return 0; +} + +static int parse_cmd_update_interval(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + if (val == 0) { + plog_err("Minimum update interval is 1 ms\n"); + } + else { + plog_info("Setting update interval to %d ms\n", val); + set_update_interval(val); + } + return 0; +} + +static int parse_cmd_mem_info(const char *str, struct input *input) +{ + if (strcmp("", str) != 0) { + return -1; + } + + cmd_mem_stats(); + cmd_mem_layout(); + return 0; +} + +static int parse_cmd_tot_ierrors_tot(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + struct global_stats_sample *gsl = stats_get_global_stats(1); + uint64_t tot = gsl->nics_ierrors; + uint64_t last_tsc = gsl->tsc; + + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), + "%"PRIu64",%"PRIu64",%"PRIu64"\n", + tot, last_tsc, rte_get_tsc_hz()); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("ierrors: %"PRIu64"\n", tot); + } + return 0; +} + +static int parse_cmd_tot_imissed_tot(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + struct global_stats_sample *gsl = stats_get_global_stats(1); + uint64_t tot = gsl->nics_imissed; + uint64_t last_tsc = gsl->tsc; + + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), + "%"PRIu64",%"PRIu64",%"PRIu64"\n", + tot, last_tsc, rte_get_tsc_hz()); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("imissed: %"PRIu64"\n", tot); + } + return 0; +} + +static int parse_cmd_reset_port(const char *str, struct input *input) +{ + uint32_t port_id; + + if (sscanf(str, "%u", &port_id ) != 1) { + return -1; + } + + cmd_reset_port(port_id); + return 0; +} + +static int parse_cmd_write_reg(const char *str, struct input *input) +{ + uint32_t port_id; + uint32_t id, val; + + if (sscanf(str, "%u %x %u", &port_id, &id, &val) != 3) { + return -1; + } + + cmd_write_reg(port_id, id, val); + return 0; +} + +static int parse_cmd_read_reg(const char *str, struct input *input) +{ + uint32_t port_id; + uint32_t id; + + if (sscanf(str, "%u %x", &port_id, &id) != 2) { + return -1; + } + + cmd_read_reg(port_id, id); + return 0; +} + +static int parse_cmd_cache_reset(const char *str, struct input *input) +{ + cmd_cache_reset(); + return 0; +} + +static int parse_cmd_set_cache_class_mask(const char *str, struct input *input) +{ + uint32_t lcore_id; + uint32_t set; + uint32_t val; + + if (sscanf(str, "%u %u %u", &lcore_id, &set, &val) != 3) { + return -1; + } + + cmd_set_cache_class_mask(lcore_id, set, val); + return 0; +} + +static int parse_cmd_set_cache_class(const char *str, struct input *input) +{ + uint32_t lcore_id; + uint32_t set; + + if (sscanf(str, "%u %u", &lcore_id, &set) != 2) { + return -1; + } + + cmd_set_cache_class(lcore_id, set); + return 0; +} + +static int parse_cmd_get_cache_class_mask(const char *str, struct input *input) +{ + uint32_t lcore_id; + uint32_t set; + uint32_t val = 0; + + if (sscanf(str, "%u %u", &lcore_id, &set) != 2) { + return -1; + } + + cmd_get_cache_class_mask(lcore_id, set, &val); + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), "%d, %d, %x\n", lcore_id, set, val); + input->reply(input, buf, strlen(buf)); + } else { + plog_info("core=%d, set=%d, mask=%x\n", lcore_id, set, val); + } + return 0; +} + +static int parse_cmd_get_cache_class(const char *str, struct input *input) +{ + uint32_t lcore_id; + uint32_t set; + uint32_t val; + + if (sscanf(str, "%u", &lcore_id) != 1) { + return -1; + } + + cmd_get_cache_class(lcore_id, &set); + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), "%d, %d\n", lcore_id, set); + input->reply(input, buf, strlen(buf)); + } else { + plog_info("core=%d, cos=%d\n", lcore_id, set); + } + return 0; +} + +static int parse_cmd_get_cache_mask(const char *str, struct input *input) +{ + uint32_t lcore_id; + uint32_t set; + uint32_t mask; + + if (sscanf(str, "%u", &lcore_id) != 1) { + return -1; + } + + cmd_get_cache_class(lcore_id, &set); + cmd_get_cache_class_mask(lcore_id, set, &mask); + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), "%d, %x\n", lcore_id, mask); + input->reply(input, buf, strlen(buf)); + } else { + plog_info("core=%d, mask=%x\n", lcore_id, mask); + } + return 0; +} + +static int parse_cmd_set_vlan_offload(const char *str, struct input *input) +{ + uint32_t port_id; + uint32_t val; + + if (sscanf(str, "%u %u", &port_id, &val) != 2) { + return -1; + } + + cmd_set_vlan_offload(port_id, val); + return 0; +} + +static int parse_cmd_set_vlan_filter(const char *str, struct input *input) +{ + uint32_t port_id; + uint32_t id, val; + + if (sscanf(str, "%u %d %u", &port_id, &id, &val) != 3) { + return -1; + } + + cmd_set_vlan_filter(port_id, id, val); + return 0; +} + +static int parse_cmd_ring_info_all(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + cmd_ringinfo_all(); + return 0; +} + +static int parse_cmd_port_up(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + cmd_port_up(val); + return 0; +} + +static int parse_cmd_port_down(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + cmd_port_down(val); + return 0; +} + +static int parse_cmd_port_link_state(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + if (!port_is_active(val)) + return -1; + + int active = prox_port_cfg[val].link_up; + const char *state = active? "up\n" : "down\n"; + + if (input->reply) + input->reply(input, state, strlen(state)); + else + plog_info("%s", state); + + return 0; +} + +static int parse_cmd_xstats(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + cmd_xstats(val); + return 0; +} + +static int parse_cmd_stats(const char *str, struct input *input) +{ + if (strcmp(str, "") == 0) + return -1; + + char buf[32768]; + char ret2[32768]; + char *ret = ret2; + int list = 0; + + strncpy(buf, str, sizeof(buf) - 1); + char *tok; + uint64_t stat_val; + + while ((tok = strchr(str, ','))) { + *tok = 0; + stat_val = stats_parser_get(str); + + ret += sprintf(ret, "%s%"PRIu64"", list? "," :"", stat_val); + list = 1; + str = tok + 1; + } + + stat_val = stats_parser_get(str); + ret += sprintf(ret, "%s%"PRIu64"", list? "," :"", stat_val); + + sprintf(ret, "\n"); + + if (input->reply) + input->reply(input, ret2, strlen(ret2)); + else + plog_info("%s", ret2); + return 0; +} + +static void replace_char(char *str, char to_replace, char by) +{ + for (size_t i = 0; str[i] != '\0'; ++i) { + if (str[i] == to_replace) + str[i] = by; + } +} + +static int parse_cmd_port_info(const char *str, struct input *input) +{ + int val; + + if (strcmp(str, "all") == 0) { + val = -1; + } + else if (sscanf(str, "%d", &val) != 1) { + return -1; + } + + char port_info[2048]; + + cmd_portinfo(val, port_info, sizeof(port_info)); + + if (input->reply) { + replace_char(port_info, '\n', ','); + port_info[strlen(port_info) - 1] = '\n'; + input->reply(input, port_info, strlen(port_info)); + } else + plog_info("%s", port_info); + + return 0; +} + +static int parse_cmd_ring_info(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + cmd_ringinfo(lcores[i], task_id); + } + } + return 0; +} + +static int parse_cmd_port_stats(const char *str, struct input *input) +{ + unsigned val; + + if (sscanf(str, "%u", &val) != 1) { + return -1; + } + + struct get_port_stats s; + if (stats_port(val, &s)) { + plog_err("Invalid port %u\n", val); + return 0; + } + char buf[256]; + snprintf(buf, sizeof(buf), + "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"," + "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"," + "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"\n", + s.no_mbufs_diff, s.ierrors_diff + s.imissed_diff, + s.rx_bytes_diff, s.tx_bytes_diff, + s.rx_pkts_diff, s.tx_pkts_diff, + s.rx_tot, s.tx_tot, + s.no_mbufs_tot, s.ierrors_tot + s.imissed_tot, + s.last_tsc, s.prev_tsc); + plog_info("%s", buf); + if (input->reply) + input->reply(input, buf, strlen(buf)); + return 0; +} + +static int parse_cmd_core_stats(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + uint64_t tot_rx = stats_core_task_tot_rx(lcore_id, task_id); + uint64_t tot_tx = stats_core_task_tot_tx(lcore_id, task_id); + uint64_t tot_drop = stats_core_task_tot_drop(lcore_id, task_id); + uint64_t last_tsc = stats_core_task_last_tsc(lcore_id, task_id); + + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), + "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"\n", + tot_rx, tot_tx, tot_drop, last_tsc, rte_get_tsc_hz()); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("RX: %"PRIu64", TX: %"PRIu64", DROP: %"PRIu64"\n", + tot_rx, tot_tx, tot_drop); + } + } + } + return 0; +} + +static int parse_cmd_lat_stats(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "lat", "")) { + plog_err("Core %u task %u is not measuring latency\n", lcore_id, task_id); + } + else { + struct stats_latency *stats = stats_latency_find(lcore_id, task_id); + struct stats_latency *tot = stats_latency_tot_find(lcore_id, task_id); + + uint64_t last_tsc = stats_core_task_last_tsc(lcore_id, task_id); + uint64_t lat_min_usec = time_unit_to_usec(&stats->min.time); + uint64_t lat_max_usec = time_unit_to_usec(&stats->max.time); + uint64_t tot_lat_min_usec = time_unit_to_usec(&tot->min.time); + uint64_t tot_lat_max_usec = time_unit_to_usec(&tot->max.time); + uint64_t lat_avg_usec = time_unit_to_usec(&stats->avg.time); + + if (input->reply) { + char buf[128]; + snprintf(buf, sizeof(buf), + "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"\n", + lat_min_usec, + lat_max_usec, + lat_avg_usec, + tot_lat_min_usec, + tot_lat_max_usec, + last_tsc, + rte_get_tsc_hz()); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("min: %"PRIu64", max: %"PRIu64", avg: %"PRIu64", min since reset: %"PRIu64", max since reset: %"PRIu64"\n", + lat_min_usec, + lat_max_usec, + lat_avg_usec, + tot_lat_min_usec, + tot_lat_max_usec); + } + } + } + } + return 0; +} + +static int parse_cmd_irq(const char *str, struct input *input) +{ + unsigned int i, c; + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (c = 0; c < nb_cores; c++) { + lcore_id = lcores[c]; + if (!task_is_mode(lcore_id, task_id, "irq", "")) { + plog_err("Core %u task %u is not in irq mode\n", lcore_id, task_id); + } else { + struct task_irq *task_irq = (struct task_irq *)(lcore_cfg[lcore_id].tasks_all[task_id]); + + task_irq_show_stats(task_irq, input); + } + } + } + return 0; +} + +static void task_lat_show_latency_histogram(uint8_t lcore_id, uint8_t task_id, struct input *input) +{ +#ifdef LATENCY_HISTOGRAM + uint64_t *buckets; + + stats_core_lat_histogram(lcore_id, task_id, &buckets); + + if (buckets == NULL) + return; + + if (input->reply) { + char buf[4096] = {0}; + for (size_t i = 0; i < 128; i++) + sprintf(buf+strlen(buf), "Bucket [%zu]: %"PRIu64"\n", i, buckets[i]); + input->reply(input, buf, strlen(buf)); + } + else { + for (size_t i = 0; i < 128; i++) + if (buckets[i]) + plog_info("Bucket [%zu]: %"PRIu64"\n", i, buckets[i]); + } +#else + plog_info("LATENCY_DETAILS disabled\n"); +#endif +} + +static int parse_cmd_lat_packets(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + if (!task_is_mode(lcore_id, task_id, "lat", "")) { + plog_err("Core %u task %u is not measuring latency\n", lcore_id, task_id); + } + else { + task_lat_show_latency_histogram(lcore_id, task_id, input); + } + } + } + return 0; +} + +static int parse_cmd_cgnat_public_hash(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + + if (!task_is_mode(lcore_id, task_id, "cgnat", "")) { + plog_err("Core %u task %u is not cgnat\n", lcore_id, task_id); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + task_cgnat_dump_public_hash((struct task_nat *)tbase); + } + } + } + return 0; +} + +static int parse_cmd_cgnat_private_hash(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + uint32_t val; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + + if (!task_is_mode(lcore_id, task_id, "cgnat", "")) { + plog_err("Core %u task %u is not cgnat\n", lcore_id, task_id); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + task_cgnat_dump_private_hash((struct task_nat *)tbase); + } + } + } + return 0; +} + +static int parse_cmd_accuracy(const char *str, struct input *input) +{ + unsigned lcores[RTE_MAX_LCORE], lcore_id, task_id, nb_cores; + uint32_t val; + + if (parse_core_task(str, lcores, &task_id, &nb_cores)) + return -1; + if (!(str = strchr_skip_twice(str, ' '))) + return -1; + if (sscanf(str, "%"PRIu32"", &val) != 1) + return -1; + + if (cores_task_are_valid(lcores, task_id, nb_cores)) { + for (unsigned int i = 0; i < nb_cores; i++) { + lcore_id = lcores[i]; + + if (!task_is_mode(lcore_id, task_id, "lat", "")) { + plog_err("Core %u task %u is not measuring latency\n", lcore_id, task_id); + } + else { + struct task_base *tbase = lcore_cfg[lcore_id].tasks_all[task_id]; + + task_lat_set_accuracy_limit((struct task_lat *)tbase, val); + } + } + } + return 0; +} + +static int parse_cmd_rx_tx_info(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + cmd_rx_tx_info(); + return 0; +} + +static int parse_cmd_version(const char *str, struct input *input) +{ + if (strcmp(str, "") != 0) { + return -1; + } + + if (input->reply) { + uint64_t version = + ((uint64_t)VERSION_MAJOR) << 24 | + ((uint64_t)VERSION_MINOR) << 16 | + ((uint64_t)VERSION_REV) << 8; + + char buf[128]; + snprintf(buf, sizeof(buf), "%"PRIu64",%"PRIu64"\n", version, (uint64_t)RTE_VERSION); + input->reply(input, buf, strlen(buf)); + } + else { + plog_info("prox version: %d.%d, DPDK version: %s\n", + VERSION_MAJOR, VERSION_MINOR, + rte_version() + sizeof(RTE_VER_PREFIX)); + } + return 0; +} + +struct cmd_str { + const char *cmd; + const char *args; + const char *help; + int (*parse)(const char *args, struct input *input); +}; + +static int parse_cmd_help(const char *str, struct input *input); + +static struct cmd_str cmd_strings[] = { + {"history", "", "Print command history", parse_cmd_history}, + {"echo", "", "echo parameter, useful to resolving variables", parse_cmd_echo}, + {"quit", "", "Stop all cores and quit", parse_cmd_quit}, + {"quit_force", "", "Quit without waiting on cores to stop", parse_cmd_quit_force}, + {"help", "<substr>", "Show list of commands that have <substr> as a substring. If no substring is provided, all commands are shown.", parse_cmd_help}, + {"verbose", "<level>", "Set verbosity level", parse_cmd_verbose}, + {"thread info", "<core_id> <task_id>", "", parse_cmd_thread_info}, + {"mem info", "", "Show information about system memory (number of huge pages and addresses of these huge pages)", parse_cmd_mem_info}, + {"update interval", "<value>", "Update statistics refresh rate, in msec (must be >=10). Default is 1 second", parse_cmd_update_interval}, + {"rx tx info", "", "Print connections between tasks on all cores", parse_cmd_rx_tx_info}, + {"start", "<core list>|all <task_id>", "Start core <core_id> or all cores", parse_cmd_start}, + {"stop", "<core list>|all <task_id>", "Stop core <core id> or all cores", parse_cmd_stop}, + + {"dump", "<core id> <task id> <nb packets>", "Create a hex dump of <nb_packets> from <task_id> on <core_id> showing how packets have changed between RX and TX.", parse_cmd_trace}, + {"dump_rx", "<core id> <task id> <nb packets>", "Create a hex dump of <nb_packets> from <task_id> on <core_id> at RX", parse_cmd_dump_rx}, + {"dump_tx", "<core id> <task id> <nb packets>", "Create a hex dump of <nb_packets> from <task_id> on <core_id> at TX", parse_cmd_dump_tx}, + {"rx distr start", "", "Start gathering statistical distribution of received packets", parse_cmd_rx_distr_start}, + {"rx distr stop", "", "Stop gathering statistical distribution of received packets", parse_cmd_rx_distr_stop}, + {"rx distr reset", "", "Reset gathered statistical distribution of received packets", parse_cmd_rx_distr_reset}, + {"rx distr show", "", "Display gathered statistical distribution of received packets", parse_cmd_rx_distr_show}, + {"tx distr start", "", "Start gathering statistical distribution of xmitted packets", parse_cmd_tx_distr_start}, + {"tx distr stop", "", "Stop gathering statistical distribution of xmitted packets", parse_cmd_tx_distr_stop}, + {"tx distr reset", "", "Reset gathered statistical distribution of xmitted packets", parse_cmd_tx_distr_reset}, + {"tx distr show", "", "Display gathered statistical distribution of xmitted packets", parse_cmd_tx_distr_show}, + + {"rate", "<port id> <queue id> <rate>", "rate does not include preamble, SFD and IFG", parse_cmd_rate}, + {"count","<core id> <task id> <count>", "Generate <count> packets", parse_cmd_count}, + {"bypass", "<core_id> <task_id>", "Bypass task", parse_cmd_bypass}, + {"reconnect", "<core_id> <task_id>", "Reconnect task", parse_cmd_reconnect}, + {"pkt_size", "<core_id> <task_id> <pkt_size>", "Set the packet size to <pkt_size>", parse_cmd_pkt_size}, + {"speed", "<core_id> <task_id> <speed percentage>", "Change the speed to <speed percentage> at which packets are being generated on core <core_id> in task <task_id>.", parse_cmd_speed}, + {"speed_byte", "<core_id> <task_id> <speed>", "Change speed to <speed>. The speed is specified in units of bytes per second.", parse_cmd_speed_byte}, + {"set value", "<core_id> <task_id> <offset> <value> <value_len>", "Set <value_len> bytes to <value> at offset <offset> in packets generated on <core_id> <task_id>", parse_cmd_set_value}, + {"set random", "<core_id> <task_id> <offset> <random_str> <value_len>", "Set <value_len> bytes to <rand_str> at offset <offset> in packets generated on <core_id> <task_id>", parse_cmd_set_random}, + {"reset values all", "", "Undo all \"set value\" commands on all cores/tasks", parse_cmd_reset_values_all}, + {"reset randoms all", "", "Undo all \"set random\" commands on all cores/tasks", parse_cmd_reset_randoms_all}, + {"reset values", "<core id> <task id>", "Undo all \"set value\" commands on specified core/task", parse_cmd_reset_values}, + + {"arp add", "<core id> <task id> <port id> <gre id> <svlan> <cvlan> <ip addr> <mac addr> <user>", "Add a single ARP entry into a CPE table on <core id>/<task id>.", parse_cmd_arp_add}, + {"rule add", "<core id> <task id> svlan_id&mask cvlan_id&mask ip_proto&mask source_ip/prefix destination_ip/prefix range dport_range action", "Add a rule to the ACL table on <core id>/<task id>", parse_cmd_rule_add}, + {"route add", "<core id> <task id> <ip/prefix> <next hop id>", "Add a route to the routing table on core <core id> <task id>. Example: route add 10.0.16.0/24 9", parse_cmd_route_add}, + {"gateway ip", "<core id> <task id> <ip>", "Define/Change IP address of destination gateway on core <core id> <task id>.", parse_cmd_gateway_ip}, + {"local ip", "<core id> <task id> <ip>", "Define/Change IP address of destination gateway on core <core id> <task id>.", parse_cmd_local_ip}, + + {"pps unit", "", "Change core stats pps unit", parse_cmd_pps_unit}, + {"reset stats", "", "Reset all statistics", parse_cmd_reset_stats}, + {"reset lat stats", "", "Reset all latency statistics", parse_cmd_reset_lat_stats}, + {"tot stats", "", "Print total RX and TX packets", parse_cmd_tot_stats}, + {"tot ierrors tot", "", "Print total number of ierrors since reset", parse_cmd_tot_ierrors_tot}, + {"tot imissed tot", "", "Print total number of imissed since reset", parse_cmd_tot_imissed_tot}, + {"lat stats", "<core id> <task id>", "Print min,max,avg latency as measured during last sampling interval", parse_cmd_lat_stats}, + {"irq stats", "<core id> <task id>", "Print irq related infos", parse_cmd_irq}, + {"lat packets", "<core id> <task id>", "Print the latency for each of the last set of packets", parse_cmd_lat_packets}, + {"accuracy limit", "<core id> <task id> <nsec>", "Only consider latency of packets that were measured with an error no more than <nsec>", parse_cmd_accuracy}, + {"core stats", "<core id> <task id>", "Print rx/tx/drop for task <task id> running on core <core id>", parse_cmd_core_stats}, + {"port_stats", "<port id>", "Print rate for no_mbufs, ierrors + imissed, rx_bytes, tx_bytes, rx_pkts, tx_pkts; totals for RX, TX, no_mbufs, ierrors + imissed for port <port id>", parse_cmd_port_stats}, + {"read reg", "", "Read register", parse_cmd_read_reg}, + {"write reg", "", "Read register", parse_cmd_write_reg}, + {"set vlan offload", "", "Set Vlan offload", parse_cmd_set_vlan_offload}, + {"set vlan filter", "", "Set Vlan filter", parse_cmd_set_vlan_filter}, + {"reset cache", "", "Reset cache", parse_cmd_cache_reset}, + {"set cache class mask", "<core id> <class> <mask>", "Set cache class mask for <core id>", parse_cmd_set_cache_class_mask}, + {"get cache class mask", "<core id> <class>", "Get cache class mask", parse_cmd_get_cache_class_mask}, + {"set cache class", "<core id> <class>", "Set cache class", parse_cmd_set_cache_class}, + {"get cache class", "<core id>", "Get cache class", parse_cmd_get_cache_class}, + {"get cache mask", "<core id>", "Get cache mask", parse_cmd_get_cache_mask}, + {"reset port", "", "Reset port", parse_cmd_reset_port}, + {"ring info all", "", "Get information about ring, such as ring size and number of elements in the ring", parse_cmd_ring_info_all}, + {"ring info", "<core id> <task id>", "Get information about ring on core <core id> in task <task id>, such as ring size and number of elements in the ring", parse_cmd_ring_info}, + {"port info", "<port id> [brief?]", "Get port related information, such as MAC address, socket, number of descriptors..., . Adding \"brief\" after command prints short version of output.", parse_cmd_port_info}, + {"port up", "<port id>", "Set the port up", parse_cmd_port_up}, + {"port down", "<port id>", "Set the port down", parse_cmd_port_down}, + {"port link state", "<port id>", "Get link state (up or down) for port", parse_cmd_port_link_state}, + {"port xstats", "<port id>", "Get extra statistics for the port", parse_cmd_xstats}, + {"stats", "<stats_path>", "Get stats as sepcified by <stats_path>. A comma-separated list of <stats_path> can be supplied", parse_cmd_stats}, + {"cgnat dump public hash", "<core id> <task id>", "Dump cgnat public hash table", parse_cmd_cgnat_public_hash}, + {"cgnat dump private hash", "<core id> <task id>", "Dump cgnat private hash table", parse_cmd_cgnat_private_hash}, + {"delay_us", "<core_id> <task_id> <delay_us>", "Set the delay in usec for the impair mode to <delay_us>", parse_cmd_delay_us}, + {"random delay_us", "<core_id> <task_id> <random delay_us>", "Set the delay in usec for the impair mode to <random delay_us>", parse_cmd_random_delay_us}, + {"probability", "<core_id> <task_id> <probability>", "Set the percent of forwarded packets for the impair mode", parse_cmd_set_probability}, + {"version", "", "Show version", parse_cmd_version}, + {0,0,0,0}, +}; + +static int parse_cmd_help(const char *str, struct input *input) +{ + /* str contains the arguments, all commands that have str as a + substring will be shown. */ + size_t len, len2, longest_cmd = 0; + for (size_t i = 0; i < cmd_parser_n_cmd(); ++i) { + if (longest_cmd <strlen(cmd_strings[i].cmd)) + longest_cmd = strlen(cmd_strings[i].cmd); + } + /* A single call to log will be executed after the help string + has been built. The reason for this is to make use of the + built-in pager. */ + char buf[32768] = {0}; + + for (size_t i = 0; i < cmd_parser_n_cmd(); ++i) { + int is_substr = 0; + const size_t cmd_len = strlen(cmd_strings[i].cmd); + for (size_t j = 0; j < cmd_len; ++j) { + is_substr = 1; + for (size_t k = 0; k < strlen(str); ++k) { + if (str[k] != (cmd_strings[i].cmd + j)[k]) { + is_substr = 0; + break; + } + } + if (is_substr) + break; + } + if (!is_substr) + continue; + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s", cmd_strings[i].cmd); + len = strlen(cmd_strings[i].cmd); + while (len < longest_cmd) { + len++; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " "); + } + + if (strlen(cmd_strings[i].args)) { + char tmp[256] = {0}; + strncpy(tmp, cmd_strings[i].args, 128); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "Arguments: %s\n", tmp); + len2 = len; + if (strlen(cmd_strings[i].help)) { + while (len2) { + len2--; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " "); + } + } + } + + if (strlen(cmd_strings[i].help)) { + int add = 0; + const char *h = cmd_strings[i].help; + do { + if (add) { + len2 = len; + while (len2) { + len2--; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " "); + } + } + char tmp[128] = {0}; + const size_t max_len = strlen(h) > 80? 80 : strlen(h); + size_t len3 = max_len; + if (len3 == 80) { + while (len3 && h[len3] != ' ') + len3--; + if (len3 == 0) + len3 = max_len; + } + + strncpy(tmp, h, len3); + h += len3; + while (h[0] == ' ' && strlen(h)) + h++; + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s\n", tmp); + add = 1; + } while(strlen(h)); + } + if (strlen(cmd_strings[i].help) == 0&& strlen(cmd_strings[i].args) == 0) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n"); + } + } + plog_info("%s", buf); + + return 0; +} + +const char *cmd_parser_cmd(size_t i) +{ + i = i < cmd_parser_n_cmd()? i: cmd_parser_n_cmd(); + return cmd_strings[i].cmd; +} + +size_t cmd_parser_n_cmd(void) +{ + return sizeof(cmd_strings)/sizeof(cmd_strings[0]) - 1; +} + +void cmd_parser_parse(const char *str, struct input *input) +{ + size_t skip; + + for (size_t i = 0; i < cmd_parser_n_cmd(); ++i) { + skip = strlen(cmd_strings[i].cmd); + if (strncmp(cmd_strings[i].cmd, str, skip) == 0 && + (str[skip] == ' ' || str[skip] == 0)) { + while (str[skip] == ' ') + skip++; + + if (cmd_strings[i].parse(str + skip, input) != 0) { + plog_warn("Invalid syntax for command '%s': %s %s\n", + cmd_strings[i].cmd, cmd_strings[i].args, cmd_strings[i].help); + } + return ; + } + } + + plog_err("Unknown command: '%s'\n", str); +} diff --git a/VNFs/DPPD-PROX/cmd_parser.h b/VNFs/DPPD-PROX/cmd_parser.h new file mode 100644 index 00000000..05284bb7 --- /dev/null +++ b/VNFs/DPPD-PROX/cmd_parser.h @@ -0,0 +1,29 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CMD_PARSER_H_ +#define _CMD_PARSER_H_ + +#include <stddef.h> + +struct input; +void cmd_parser_parse(const char *str, struct input *input); +const char *cmd_parser_cmd(size_t i); +size_t cmd_parser_n_cmd(void); +int task_is_mode(uint32_t lcore_id, uint32_t task_id, const char *mode, const char *sub_mode); +int task_is_sub_mode(uint32_t lcore_id, uint32_t task_id, const char *sub_mode); + +#endif /* _CMD_PARSER_H_ */ diff --git a/VNFs/DPPD-PROX/commands.c b/VNFs/DPPD-PROX/commands.c new file mode 100644 index 00000000..93acc62a --- /dev/null +++ b/VNFs/DPPD-PROX/commands.c @@ -0,0 +1,1016 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <rte_table_hash.h> +#include <rte_version.h> +#include <rte_malloc.h> + +#include "prox_malloc.h" +#include "display.h" +#include "commands.h" +#include "log.h" +#include "run.h" +#include "lconf.h" +#include "hash_utils.h" +#include "prox_cfg.h" +#include "prox_port_cfg.h" +#include "defines.h" +#include "handle_qos.h" +#include "handle_qinq_encap4.h" +#include "quit.h" +#include "input.h" +#include "rw_reg.h" +#include "cqm.h" +#include "stats_core.h" + +void start_core_all(int task_id) +{ + uint32_t cores[RTE_MAX_LCORE]; + uint32_t lcore_id; + char tmp[256]; + int cnt = 0; + + prox_core_to_str(tmp, sizeof(tmp), 0); + plog_info("Starting cores: %s\n", tmp); + + lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + cores[cnt++] = lcore_id; + } + start_cores(cores, cnt, task_id); +} + +void stop_core_all(int task_id) +{ + uint32_t cores[RTE_MAX_LCORE]; + uint32_t lcore_id; + char tmp[256]; + int cnt = 0; + + prox_core_to_str(tmp, sizeof(tmp), 0); + plog_info("Stopping cores: %s\n", tmp); + + lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + cores[cnt++] = lcore_id; + } + + stop_cores(cores, cnt, task_id); +} + +static void warn_inactive_cores(uint32_t *cores, int count, const char *prefix) +{ + for (int i = 0; i < count; ++i) { + if (!prox_core_active(cores[i], 0)) { + plog_warn("%s %u: core is not active\n", prefix, cores[i]); + } + } +} + +static inline int wait_command_handled(struct lcore_cfg *lconf) +{ + uint64_t t1 = rte_rdtsc(), t2; + while (lconf_is_req(lconf)) { + t2 = rte_rdtsc(); + if (t2 - t1 > 5 * rte_get_tsc_hz()) { + // Failed to handle command ... + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targs = &lconf->targs[task_id]; + if (!(targs->flags & TASK_ARG_DROP)) { + plogx_err("Failed to handle command - task is in NO_DROP and might be stuck...\n"); + return - 1; + } + } + plogx_err("Failed to handle command\n"); + return -1; + } + } + return 0; +} +void start_cores(uint32_t *cores, int count, int task_id) +{ + int n_started_cores = 0; + uint32_t started_cores[RTE_MAX_LCORE]; + + warn_inactive_cores(cores, count, "Can't start core"); + + for (int i = 0; i < count; ++i) { + struct lcore_cfg *lconf = &lcore_cfg[cores[i]]; + + if (lconf->n_tasks_run != lconf->n_tasks_all) { + + lconf->msg.type = LCONF_MSG_START; + lconf->msg.task_id = task_id; + lconf_set_req(lconf); + if (task_id == -1) + plog_info("Starting core %u (all tasks)\n", cores[i]); + else + plog_info("Starting core %u task %u\n", cores[i], task_id); + started_cores[n_started_cores++] = cores[i]; + lconf->flags |= LCONF_FLAG_RUNNING; + rte_eal_remote_launch(lconf_run, NULL, cores[i]); + } + else { + plog_warn("Core %u is already running all its tasks\n", cores[i]); + } + } + + /* This function is blocking, so detect when each core has + consumed the message. */ + for (int i = 0; i < n_started_cores; ++i) { + struct lcore_cfg *lconf = &lcore_cfg[started_cores[i]]; + plog_info("Waiting for core %u to start...", started_cores[i]); + if (wait_command_handled(lconf) == -1) return; + plog_info(" OK\n"); + } +} + +void stop_cores(uint32_t *cores, int count, int task_id) +{ + int n_stopped_cores = 0; + uint32_t stopped_cores[RTE_MAX_LCORE]; + uint32_t c; + + warn_inactive_cores(cores, count, "Can't stop core"); + + for (int i = 0; i < count; ++i) { + struct lcore_cfg *lconf = &lcore_cfg[cores[i]]; + if (lconf->n_tasks_run) { + if (wait_command_handled(lconf) == -1) return; + + lconf->msg.type = LCONF_MSG_STOP; + lconf->msg.task_id = task_id; + lconf_set_req(lconf); + stopped_cores[n_stopped_cores++] = cores[i]; + } + } + + for (int i = 0; i < n_stopped_cores; ++i) { + c = stopped_cores[i]; + struct lcore_cfg *lconf = &lcore_cfg[c]; + if (wait_command_handled(lconf) == -1) return; + + if (lconf->n_tasks_run == 0) { + plog_info("All tasks stopped on core %u, waiting for core to stop...", c); + rte_eal_wait_lcore(c); + plog_info(" OK\n"); + lconf->flags &= ~LCONF_FLAG_RUNNING; + } + else { + plog_info("Stopped task %u on core %u\n", task_id, c); + } + } +} + +struct size_unit { + uint64_t val; + uint64_t frac; + char unit[8]; +}; + +static struct size_unit to_size_unit(uint64_t bytes) +{ + struct size_unit ret; + + if (bytes > 1 << 30) { + ret.val = bytes >> 30; + ret.frac = ((bytes - (ret.val << 30)) * 1000) / (1 << 30); + strcpy(ret.unit, "GB"); + } + else if (bytes > 1 << 20) { + ret.val = bytes >> 20; + ret.frac = ((bytes - (ret.val << 20)) * 1000) / (1 << 20); + strcpy(ret.unit, "MB"); + } + else if (bytes > 1 << 10) { + ret.val = bytes >> 10; + ret.frac = (bytes - (ret.val << 10)) * 1000 / (1 << 10); + strcpy(ret.unit, "KB"); + } + else { + ret.val = bytes; + ret.frac = 0; + strcpy(ret.unit, "B"); + } + + return ret; +} + +void cmd_mem_stats(void) +{ + struct rte_malloc_socket_stats sock_stats; + uint64_t v; + struct size_unit su; + + for (uint32_t i = 0; i < RTE_MAX_NUMA_NODES; ++i) { + if (rte_malloc_get_socket_stats(i, &sock_stats) < 0 || sock_stats.heap_totalsz_bytes == 0) + continue; + + plogx_info("Socket %u memory stats:\n", i); + su = to_size_unit(sock_stats.heap_totalsz_bytes); + plogx_info("\tHeap_size: %zu.%03zu %s\n", su.val, su.frac, su.unit); + su = to_size_unit(sock_stats.heap_freesz_bytes); + plogx_info("\tFree_size: %zu.%03zu %s\n", su.val, su.frac, su.unit); + su = to_size_unit(sock_stats.heap_allocsz_bytes); + plogx_info("\tAlloc_size: %zu.%03zu %s\n", su.val, su.frac, su.unit); + su = to_size_unit(sock_stats.greatest_free_size); + plogx_info("\tGreatest_free_size: %zu %s\n", su.val, su.unit); + plogx_info("\tAlloc_count: %u\n", sock_stats.alloc_count); + plogx_info("\tFree_count: %u\n", sock_stats.free_count); + } +} + +void cmd_mem_layout(void) +{ + const struct rte_memseg* memseg = rte_eal_get_physmem_layout(); + + plog_info("Memory layout:\n"); + for (uint32_t i = 0; i < RTE_MAX_MEMSEG; i++) { + if (memseg[i].addr == NULL) + break; + + const char *sz_str; + switch (memseg[i].hugepage_sz >> 20) { + case 2: + sz_str = "2MB"; + break; + case 1024: + sz_str = "1GB"; + break; + default: + sz_str = "??"; + } + + plog_info("Segment %u: [%#lx-%#lx] at %p using %zu pages of %s\n", + i, + memseg[i].phys_addr, + memseg[i].phys_addr + memseg[i].len, + memseg[i].addr, + memseg[i].len/memseg[i].hugepage_sz, sz_str); + } +} + +void cmd_dump(uint8_t lcore_id, uint8_t task_id, uint32_t nb_packets, struct input *input, int rx, int tx) +{ + plog_info("dump %u %u %u\n", lcore_id, task_id, nb_packets); + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } + else if (task_id >= lcore_cfg[lcore_id].n_tasks_all) { + plog_warn("task_id too high, should be in [0, %u]\n", lcore_cfg[lcore_id].n_tasks_all - 1); + } + else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + lconf->tasks_all[task_id]->aux->task_rt_dump.input = input; + + if (wait_command_handled(lconf) == -1) return; + if (rx && tx) + lconf->msg.type = LCONF_MSG_DUMP; + else if (rx) + lconf->msg.type = LCONF_MSG_DUMP_RX; + else if (tx) + lconf->msg.type = LCONF_MSG_DUMP_TX; + + if (rx || tx) { + lconf->msg.task_id = task_id; + lconf->msg.val = nb_packets; + lconf_set_req(lconf); + } + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_trace(uint8_t lcore_id, uint8_t task_id, uint32_t nb_packets) +{ + plog_info("trace %u %u %u\n", lcore_id, task_id, nb_packets); + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } + else if (task_id >= lcore_cfg[lcore_id].n_tasks_all) { + plog_warn("task_id too high, should be in [0, %u]\n", lcore_cfg[lcore_id].n_tasks_all - 1); + } + else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + + lconf->msg.type = LCONF_MSG_TRACE; + lconf->msg.task_id = task_id; + lconf->msg.val = nb_packets; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_rx_bw_start(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (lcore_cfg[lcore_id].flags & LCONF_FLAG_RX_BW_ACTIVE) { + plog_warn("rx bandwidt already on core %u\n", lcore_id); + } else { + + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_RX_BW_START; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_tx_bw_start(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (lcore_cfg[lcore_id].flags & LCONF_FLAG_TX_BW_ACTIVE) { + plog_warn("tx bandwidth already running on core %u\n", lcore_id); + } else { + + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_TX_BW_START; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_rx_bw_stop(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (!(lcore_cfg[lcore_id].flags & LCONF_FLAG_RX_BW_ACTIVE)) { + plog_warn("rx bandwidth not running on core %u\n", lcore_id); + } else { + + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_RX_BW_STOP; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_tx_bw_stop(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (!(lcore_cfg[lcore_id].flags & LCONF_FLAG_TX_BW_ACTIVE)) { + plog_warn("tx bandwidth not running on core %u\n", lcore_id); + } else { + + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_TX_BW_STOP; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} +void cmd_rx_distr_start(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (lcore_cfg[lcore_id].flags & LCONF_FLAG_RX_DISTR_ACTIVE) { + plog_warn("rx distribution already xrunning on core %u\n", lcore_id); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_RX_DISTR_START; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_tx_distr_start(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if (lcore_cfg[lcore_id].flags & LCONF_FLAG_TX_DISTR_ACTIVE) { + plog_warn("tx distribution already xrunning on core %u\n", lcore_id); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_TX_DISTR_START; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_rx_distr_stop(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if ((lcore_cfg[lcore_id].flags & LCONF_FLAG_RX_DISTR_ACTIVE) == 0) { + plog_warn("rx distribution not running on core %u\n", lcore_id); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_RX_DISTR_STOP; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_tx_distr_stop(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else if ((lcore_cfg[lcore_id].flags & LCONF_FLAG_TX_DISTR_ACTIVE) == 0) { + plog_warn("tx distribution not running on core %u\n", lcore_id); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_TX_DISTR_STOP; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_rx_distr_rst(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_RX_DISTR_RESET; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_tx_distr_rst(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (wait_command_handled(lconf) == -1) return; + lconf->msg.type = LCONF_MSG_TX_DISTR_RESET; + lconf_set_req(lconf); + + if (lconf->n_tasks_run == 0) { + lconf_do_flags(lconf); + } + } +} + +void cmd_rx_distr_show(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else { + for (uint32_t i = 0; i < lcore_cfg[lcore_id].n_tasks_all; ++i) { + struct task_base *t = lcore_cfg[lcore_id].tasks_all[i]; + plog_info("t[%u]: ", i); + for (uint32_t j = 0; j < sizeof(t->aux->rx_bucket)/sizeof(t->aux->rx_bucket[0]); ++j) { + plog_info("%u ", t->aux->rx_bucket[j]); + } + plog_info("\n"); + } + } +} +void cmd_tx_distr_show(uint32_t lcore_id) +{ + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } else { + for (uint32_t i = 0; i < lcore_cfg[lcore_id].n_tasks_all; ++i) { + struct task_base *t = lcore_cfg[lcore_id].tasks_all[i]; + uint64_t tot = 0, avg = 0; + for (uint32_t j = 0; j < sizeof(t->aux->tx_bucket)/sizeof(t->aux->tx_bucket[0]); ++j) { + tot += t->aux->tx_bucket[j]; + avg += j * t->aux->tx_bucket[j]; + } + if (tot) { + avg = avg / tot; + } + plog_info("t[%u]: %lu: ", i, avg); + for (uint32_t j = 0; j < sizeof(t->aux->tx_bucket)/sizeof(t->aux->tx_bucket[0]); ++j) { + plog_info("%u ", t->aux->tx_bucket[j]); + } + plog_info("\n"); + } + } +} + +void cmd_ringinfo_all(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + cmd_ringinfo(lcore_id, task_id); + } + } +} + +void cmd_ringinfo(uint8_t lcore_id, uint8_t task_id) +{ + struct lcore_cfg *lconf; + struct rte_ring *ring; + struct task_args* targ; + uint32_t count; + + if (!prox_core_active(lcore_id, 0)) { + plog_info("lcore %u is not active\n", lcore_id); + return; + } + lconf = &lcore_cfg[lcore_id]; + if (task_id >= lconf->n_tasks_all) { + plog_warn("Invalid task index %u: lcore %u has %u tasks\n", task_id, lcore_id, lconf->n_tasks_all); + return; + } + + targ = &lconf->targs[task_id]; + plog_info("Core %u task %u: %u rings\n", lcore_id, task_id, targ->nb_rxrings); + for (uint8_t i = 0; i < targ->nb_rxrings; ++i) { + ring = targ->rx_rings[i]; +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + count = ring->prod.mask + 1; +#else + count = ring->mask + 1; +#endif + plog_info("\tRing %u:\n", i); + plog_info("\t\tFlags: %s,%s\n", ring->flags & RING_F_SP_ENQ? "sp":"mp", ring->flags & RING_F_SC_DEQ? "sc":"mc"); + plog_info("\t\tMemory size: %zu bytes\n", rte_ring_get_memsize(count)); + plog_info("\t\tOccupied: %u/%u\n", rte_ring_count(ring), count); + } +} + +void cmd_port_up(uint8_t port_id) +{ + int err; + + if (!port_is_active(port_id)) { + return ; + } + + if ((err = rte_eth_dev_set_link_up(port_id)) == 0) { + plog_info("Bringing port %d up\n", port_id); + } + else { + plog_warn("Failed to bring port %d up with error %d\n", port_id, err); + } +} + +void cmd_port_down(uint8_t port_id) +{ + int err; + + if (!port_is_active(port_id)) { + return ; + } + + if ((err = rte_eth_dev_set_link_down(port_id)) == 0) { + plog_info("Bringing port %d down\n", port_id); + } + else { + plog_warn("Failed to bring port %d down with error %d\n", port_id, err); + } +} + +void cmd_xstats(uint8_t port_id) +{ +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) + int n_xstats; + struct rte_eth_xstat *eth_xstat = NULL; // id and value + struct rte_eth_xstat_name *eth_xstat_name = NULL; // only names + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + int rc; + + n_xstats = rte_eth_xstats_get(port_id, NULL, 0); + eth_xstat_name = prox_zmalloc(n_xstats * sizeof(*eth_xstat_name), port_cfg->socket); + PROX_ASSERT(eth_xstat_name); + rc = rte_eth_xstats_get_names(port_id, eth_xstat_name, n_xstats); + if ((rc < 0) || (rc > n_xstats)) { + if (rc < 0) { + plog_warn("Failed to get xstats_names on port %d with error %d\n", port_id, rc); + } else if (rc > n_xstats) { + plog_warn("Failed to get xstats_names on port %d: too many xstats (%d)\n", port_id, rc); + } + } + + eth_xstat = prox_zmalloc(n_xstats * sizeof(*eth_xstat), port_cfg->socket); + PROX_ASSERT(eth_xstat); + rc = rte_eth_xstats_get(port_id, eth_xstat, n_xstats); + if ((rc < 0) || (rc > n_xstats)) { + if (rc < 0) { + plog_warn("Failed to get xstats on port %d with error %d\n", port_id, rc); + } else if (rc > n_xstats) { + plog_warn("Failed to get xstats on port %d: too many xstats (%d)\n", port_id, rc); + } + } else { + for (int i=0;i<rc;i++) { + plog_info("%s: %ld\n", eth_xstat_name[i].name, eth_xstat[i].value); + } + } + if (eth_xstat_name) + prox_free(eth_xstat_name); + if (eth_xstat) + prox_free(eth_xstat); +#else +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + int n_xstats; + struct rte_eth_xstats *eth_xstats; + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + int rc; + + n_xstats = rte_eth_xstats_get(port_id, NULL, 0); + eth_xstats = prox_zmalloc(n_xstats * sizeof(*eth_xstats), port_cfg->socket); + PROX_ASSERT(eth_xstats); + rc = rte_eth_xstats_get(port_id, eth_xstats, n_xstats); + if ((rc < 0) || (rc > n_xstats)) { + if (rc < 0) { + plog_warn("Failed to get xstats on port %d with error %d\n", port_id, rc); + } else if (rc > n_xstats) { + plog_warn("Failed to get xstats on port %d: too many xstats (%d)\n", port_id, rc); + } + } else { + for (int i=0;i<rc;i++) { + plog_info("%s: %ld\n", eth_xstats[i].name, eth_xstats[i].value); + } + } + if (eth_xstats) + prox_free(eth_xstats); +#else + plog_warn("Failed to get xstats, xstats are not supported in this version of dpdk\n"); +#endif +#endif +} + +void cmd_portinfo(int port_id, char *dst, size_t max_len) +{ + char *end = dst + max_len; + + *dst = 0; + if (port_id == -1) { + uint8_t max_port_idx = prox_last_port_active() + 1; + + for (uint8_t port_id = 0; port_id < max_port_idx; ++port_id) { + if (!prox_port_cfg[port_id].active) { + continue; + } + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + + dst += snprintf(dst, end - dst, + "%2d:%10s; "MAC_BYTES_FMT"; %s\n", + port_id, + port_cfg->name, + MAC_BYTES(port_cfg->eth_addr.addr_bytes), + port_cfg->pci_addr); + } + return; + } + + if (!port_is_active(port_id)) { + return ; + } + + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + + dst += snprintf(dst, end - dst, "Port info for port %u\n", port_id); + dst += snprintf(dst, end - dst, "\tName: %s\n", port_cfg->name); + dst += snprintf(dst, end - dst, "\tDriver: %s\n", port_cfg->driver_name); + dst += snprintf(dst, end - dst, "\tMac address: "MAC_BYTES_FMT"\n", MAC_BYTES(port_cfg->eth_addr.addr_bytes)); + dst += snprintf(dst, end - dst, "\tLink speed: %u Mbps\n", port_cfg->link_speed); + dst += snprintf(dst, end - dst, "\tLink status: %s\n", port_cfg->link_up? "up" : "down"); + dst += snprintf(dst, end - dst, "\tSocket: %u\n", port_cfg->socket); + dst += snprintf(dst, end - dst, "\tPCI address: %s\n", port_cfg->pci_addr); + dst += snprintf(dst, end - dst, "\tPromiscuous: %s\n", port_cfg->promiscuous? "yes" : "no"); + dst += snprintf(dst, end - dst, "\tNumber of RX/TX descriptors: %u/%u\n", port_cfg->n_rxd, port_cfg->n_txd); + dst += snprintf(dst, end - dst, "\tNumber of RX/TX queues: %u/%u (max: %u/%u)\n", port_cfg->n_rxq, port_cfg->n_txq, port_cfg->max_rxq, port_cfg->max_txq); + dst += snprintf(dst, end - dst, "\tMemory pools:\n"); + + for (uint8_t i = 0; i < 32; ++i) { + if (port_cfg->pool[i]) { + dst += snprintf(dst, end - dst, "\t\tname: %s (%p)\n", + port_cfg->pool[i]->name, port_cfg->pool[i]); + } + } +} + +void cmd_read_reg(uint8_t port_id, unsigned int id) +{ + unsigned int val, rc; + if (!port_is_active(port_id)) { + return ; + } + rc = read_reg(port_id, id, &val); + if (rc) { + plog_warn("Failed to read register %d on port %d\n", id, port_id); + } + else { + plog_info("Register 0x%08X : %08X \n", id, val); + } +} + +void cmd_reset_port(uint8_t portid) +{ + unsigned int rc; + if (!prox_port_cfg[portid].active) { + plog_info("port not active \n"); + return; + } + rte_eth_dev_stop(portid); + rc = rte_eth_dev_start(portid); + if (rc) { + plog_warn("Failed to restart port %d\n", portid); + } +} +void cmd_write_reg(uint8_t port_id, unsigned int id, unsigned int val) +{ + if (!port_is_active(port_id)) { + return ; + } + + plog_info("writing 0x%08X %08X\n", id, val); + write_reg(port_id, id, val); +} + +void cmd_set_vlan_offload(uint8_t port_id, unsigned int val) +{ + if (!port_is_active(port_id)) { + return ; + } + + plog_info("setting vlan offload to %d\n", val); + if (val & ~(ETH_VLAN_STRIP_OFFLOAD | ETH_VLAN_FILTER_OFFLOAD | ETH_VLAN_EXTEND_OFFLOAD)) { + plog_info("wrong vlan offload value\n"); + } + int ret = rte_eth_dev_set_vlan_offload(port_id, val); + plog_info("rte_eth_dev_set_vlan_offload return %d\n", ret); +} + +void cmd_set_vlan_filter(uint8_t port_id, unsigned int id, unsigned int val) +{ + if (!port_is_active(port_id)) { + return ; + } + + plog_info("setting vln filter for vlan %d to %d\n", id, val); + int ret = rte_eth_dev_vlan_filter(port_id, id, val); + plog_info("rte_eth_dev_vlan_filter return %d\n", ret); +} + +void cmd_thread_info(uint8_t lcore_id, uint8_t task_id) +{ + plog_info("thread_info %u %u \n", lcore_id, task_id); + if (lcore_id > RTE_MAX_LCORE) { + plog_warn("core_id too high, maximum allowed is: %u\n", RTE_MAX_LCORE); + } + if (!prox_core_active(lcore_id, 0)) { + plog_warn("lcore %u is not active\n", lcore_id); + return; + } + if (task_id >= lcore_cfg[lcore_id].n_tasks_all) { + plog_warn("task_id too high, should be in [0, %u]\n", lcore_cfg[lcore_id].n_tasks_all - 1); + return; + } + if (strcmp(lcore_cfg[lcore_id].targs[task_id].task_init->mode_str, "qos") == 0) { + struct task_base *task; + + task = lcore_cfg[lcore_id].tasks_all[task_id]; + plog_info("core %d, task %d: %d mbufs stored in QoS\n", lcore_id, task_id, + task_qos_n_pkts_buffered(task)); + +#ifdef ENABLE_EXTRA_USER_STATISTICS + } + else if (lcore_cfg[lcore_id].targs[task_id].mode == QINQ_ENCAP4) { + struct task_qinq_encap4 *task; + task = (struct task_qinq_encap4 *)(lcore_cfg[lcore_id].tasks_all[task_id]); + for (int i=0;i<task->n_users;i++) { + if (task->stats_per_user[i]) + plog_info("User %d: %d packets\n", i, task->stats_per_user[i]); + } +#endif + } + else { + // Only QoS thread info so far + plog_err("core %d, task %d: not a qos core (%p)\n", lcore_id, task_id, lcore_cfg[lcore_id].thread_x); + } +} + +void cmd_rx_tx_info(void) +{ + uint32_t lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + for (uint8_t task_id = 0; task_id < lcore_cfg[lcore_id].n_tasks_all; ++task_id) { + struct task_args *targ = &lcore_cfg[lcore_id].targs[task_id]; + + plog_info("Core %u:", lcore_id); + if (targ->rx_port_queue[0].port != OUT_DISCARD) { + for (int i = 0; i < targ->nb_rxports; i++) { + plog_info(" RX port %u (queue %u)", targ->rx_port_queue[i].port, targ->rx_port_queue[i].queue); + } + } + else { + for (uint8_t j = 0; j < targ->nb_rxrings; ++j) { + plog_info(" RX ring[%u,%u] %p", task_id, j, targ->rx_rings[j]); + } + } + plog_info(" ==>"); + for (uint8_t j = 0; j < targ->nb_txports; ++j) { + plog_info(" TX port %u (queue %u)", targ->tx_port_queue[j].port, + targ->tx_port_queue[j].queue); + } + + for (uint8_t j = 0; j < targ->nb_txrings; ++j) { + plog_info(" TX ring %p", targ->tx_rings[j]); + } + + plog_info("\n"); + } + } +} +void cmd_get_cache_class(uint32_t lcore_id, uint32_t *set) +{ + uint64_t tmp_rmid = 0; + cqm_assoc_read(lcore_id, &tmp_rmid); + *set = (uint32_t)(tmp_rmid >> 32); +} + +void cmd_get_cache_class_mask(uint32_t lcore_id, uint32_t set, uint32_t *val) +{ + cat_get_class_mask(lcore_id, set, val); +} + +void cmd_set_cache_class_mask(uint32_t lcore_id, uint32_t set, uint32_t val) +{ + cat_set_class_mask(lcore_id, set, val); + lcore_cfg[lcore_id].cache_set = set; + uint32_t id = -1; + while(prox_core_next(&id, 0) == 0) { + if ((lcore_cfg[id].cache_set == set) && (rte_lcore_to_socket_id(id) == rte_lcore_to_socket_id(lcore_id))) { + plog_info("Updating mask for core %d to %d\n", id, set); + stats_update_cache_mask(id, val); + } + } +} + +void cmd_set_cache_class(uint32_t lcore_id, uint32_t set) +{ + uint64_t tmp_rmid = 0; + uint32_t val = 0; + cqm_assoc_read(lcore_id, &tmp_rmid); + cqm_assoc(lcore_id, (tmp_rmid & 0xffffffff) | ((set * 1L) << 32)); + cat_get_class_mask(lcore_id, set, &val); + stats_update_cache_mask(lcore_id, val); +} + +void cmd_cache_reset(void) +{ + uint8_t sockets[MAX_SOCKETS] = {0}; + uint8_t cores[MAX_SOCKETS] = {0}; + uint32_t mask = (1 << cat_get_num_ways()) - 1; + uint32_t lcore_id = -1, socket_id; + while(prox_core_next(&lcore_id, 0) == 0) { + cqm_assoc(lcore_id, 0); + socket_id = rte_lcore_to_socket_id(lcore_id); + if (socket_id < MAX_SOCKETS) { + sockets[socket_id] = 1; + cores[socket_id] = lcore_id; + } + stats_update_cache_mask(lcore_id, mask); + plog_info("Setting core %d to cache mask %x\n", lcore_id, mask); + lcore_cfg[lcore_id].cache_set = 0; + } + for (uint32_t s = 0; s < MAX_SOCKETS; s++) { + if (sockets[s]) + cat_reset_cache(cores[s]); + } + stats_lcore_assoc_rmid(); +} + +int bypass_task(uint32_t lcore_id, uint32_t task_id) +{ + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + struct task_args *targ, *starg, *dtarg; + struct rte_ring *ring = NULL; + + if (task_id >= lconf->n_tasks_all) + return -1; + + targ = &lconf->targs[task_id]; + if (targ->nb_txrings == 1) { + plog_info("Task has %d receive and 1 transmmit ring and can be bypassed, %d precedent tasks\n", targ->nb_rxrings, targ->n_prev_tasks); + // Find source task + for (unsigned int i = 0; i < targ->n_prev_tasks; i++) { + starg = targ->prev_tasks[i]; + for (unsigned int j = 0; j < starg->nb_txrings; j++) { + for (unsigned int k = 0; k < targ->nb_rxrings; k++) { + if (starg->tx_rings[j] == targ->rx_rings[k]) { + plog_info("bypassing ring %p and connecting it to %p\n", starg->tx_rings[j], targ->tx_rings[0]); + starg->tx_rings[j] = targ->tx_rings[0]; + struct task_base *tbase = starg->tbase; + tbase->tx_params_sw.tx_rings[j] = starg->tx_rings[j]; + } + } + } + } + } else { + plog_info("Task has %d receive and %d transmit ring and cannot be bypassed\n", targ->nb_rxrings, targ->nb_txrings); + return -1; + } + + return 0; +} + +int reconnect_task(uint32_t lcore_id, uint32_t task_id) +{ + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + struct task_args *targ, *starg, *dtarg = NULL; + struct rte_ring *ring = NULL; + + if (task_id >= lconf->n_tasks_all) + return -1; + + targ = &lconf->targs[task_id]; + if (targ->nb_txrings == 1) { + // Find source task + for (unsigned int i = 0; i < targ->n_prev_tasks; i++) { + starg = targ->prev_tasks[i]; + for (unsigned int j = 0; j < starg->nb_txrings; j++) { + if (starg->tx_rings[j] == targ->tx_rings[0]) { + if (targ->n_prev_tasks == targ->nb_rxrings) { + starg->tx_rings[j] = targ->rx_rings[i]; + struct task_base *tbase = starg->tbase; + tbase->tx_params_sw.tx_rings[j] = starg->tx_rings[j]; + plog_info("Task has %d receive and 1 transmmit ring and can be reconnected, %d precedent tasks\n", targ->nb_rxrings, targ->n_prev_tasks); + } else if (targ->nb_rxrings == 1) { + starg->tx_rings[j] = targ->rx_rings[0]; + struct task_base *tbase = starg->tbase; + tbase->tx_params_sw.tx_rings[j] = starg->tx_rings[j]; + plog_info("Task has %d receive and 1 transmmit ring and ring %p can be reconnected, %d precedent tasks\n", targ->nb_rxrings, starg->tx_rings[j], targ->n_prev_tasks); + } else { + plog_err("Unexpected configuration: %d precedent tasks, %d rx rings\n", targ->n_prev_tasks, targ->nb_rxrings); + } + } + } + } + } else { + plog_info("Task has %d receive and %d transmit ring and cannot be bypassed\n", targ->nb_rxrings, targ->nb_txrings); + return -1; + } + + return 0; +} diff --git a/VNFs/DPPD-PROX/commands.h b/VNFs/DPPD-PROX/commands.h new file mode 100644 index 00000000..6c4a29a3 --- /dev/null +++ b/VNFs/DPPD-PROX/commands.h @@ -0,0 +1,70 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _COMMANDS_H_ +#define _COMMANDS_H_ + +#include <inttypes.h> + +struct input; + +/* command functions */ +void start_core_all(int task_id); +void stop_core_all(int task_id); +void start_cores(uint32_t *cores, int count, int task_id); +void stop_cores(uint32_t *cores, int count, int task_id); + +void cmd_trace(uint8_t lcore_id, uint8_t task_id, uint32_t nb_packets); +void cmd_dump(uint8_t lcore_id, uint8_t task_id, uint32_t nb_packets, struct input *input, int rx, int tx); +void cmd_mem_stats(void); +void cmd_mem_layout(void); +void cmd_hashdump(uint8_t lcore_id, uint8_t task_id, uint32_t table_id); +void cmd_rx_distr_start(uint32_t lcore_id); +void cmd_rx_distr_stop(uint32_t lcore_id); +void cmd_rx_distr_rst(uint32_t lcore_id); +void cmd_rx_distr_show(uint32_t lcore_id); +void cmd_tx_distr_start(uint32_t lcore_id); +void cmd_tx_distr_stop(uint32_t lcore_id); +void cmd_tx_distr_rst(uint32_t lcore_id); +void cmd_tx_distr_show(uint32_t lcore_id); +void cmd_rx_bw_start(uint32_t lcore_id); +void cmd_tx_bw_start(uint32_t lcore_id); +void cmd_rx_bw_stop(uint32_t lcore_id); +void cmd_tx_bw_stop(uint32_t lcore_id); + +void cmd_portinfo(int port_id, char *dst, size_t max_len); +void cmd_port_up(uint8_t port_id); +void cmd_port_down(uint8_t port_id); +void cmd_xstats(uint8_t port_id); +void cmd_thread_info(uint8_t lcore_id, uint8_t task_id); +void cmd_ringinfo(uint8_t lcore_id, uint8_t task_id); +void cmd_ringinfo_all(void); +void cmd_rx_tx_info(void); +void cmd_read_reg(uint8_t port_id, uint32_t id); +void cmd_write_reg(uint8_t port_id, unsigned int id, unsigned int val); +void cmd_set_vlan_filter(uint8_t port_id, unsigned int id, unsigned int val); +void cmd_set_vlan_offload(uint8_t port_id, unsigned int val); +void cmd_get_cache_class(uint32_t lcore_id, uint32_t *set); +void cmd_get_cache_class_mask(uint32_t lcore_id, uint32_t set, uint32_t *val); +void cmd_set_cache_class_mask(uint32_t lcore_id, uint32_t set, uint32_t val); +void cmd_set_cache_class(uint32_t lcore_id, uint32_t set); +void cmd_cache_reset(void); + +void cmd_reset_port(uint8_t port_id); +int reconnect_task(uint32_t lcore_id, uint32_t task_id); +int bypass_task(uint32_t lcore_id, uint32_t task_id); + +#endif /* _COMMANDS_H_ */ diff --git a/VNFs/DPPD-PROX/config/acl_table.lua b/VNFs/DPPD-PROX/config/acl_table.lua new file mode 100644 index 00000000..cebe3b77 --- /dev/null +++ b/VNFs/DPPD-PROX/config/acl_table.lua @@ -0,0 +1,36 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +five_tuple = function(ip_proto, src, dst, sport, dport, action) + return { + ip_proto = ip_proto, + src_cidr = src, + dst_cidr = dst, + sport = sport, + dport = dport, + action = action, + } +end + +return { + five_tuple(val_mask(17, 0xff), cidr("192.168.0.0/18"), cidr("10.10.0.0/16"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(17, 0xff), cidr("10.10.0.0/18"), cidr("192.168.0.0/16"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(17, 0xff), cidr("192.168.0.0/18"), cidr("74.0.0.0/7"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(17, 0xff), cidr("1.1.1.0/24"), cidr("1.2.3.0/24"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(17, 0xff), cidr("192.168.1.0/24"), cidr("192.168.1.0/24"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(6, 0xf), cidr("10.0.0.0/18"), cidr("192.168.0.0/16"), val_range(0,65535), val_range(0,65535), "allow"), + five_tuple(val_mask(6, 0xf), cidr("192.168.0.0/18"), cidr("10.0.0.0/16"), val_range(0,65535), val_range(0,65535), "allow"), +} diff --git a/VNFs/DPPD-PROX/config/bng-1q-4ports.cfg b/VNFs/DPPD-PROX/config/bng-1q-4ports.cfg new file mode 100644 index 00000000..661d4aa0 --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-1q-4ports.cfg @@ -0,0 +1,130 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-4ports.cfg. The difference is that on each of the interfaces, only +; one queue is used. Use-cases for this configuration include running in a +; virtualized environment using SRIOV. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=hardware +[port 1] +name=inet0 +mac=hardware +[port 2] +name=cpe1 +mac=hardware +[port 3] +name=inet1 +mac=hardware +[variables] +;$wk=3s0,3s0h; 2 workers +;$wk=3s0-4s0,3s0h-4s0h; 4 workers +;$wk=3s0-5s0,3s0h-5s0h; 6 workers +$wk=5s0-8s0,5s0h-8s0h; 8 workers +;$wk=3s0-7s0,3s0h-7s0h; 10 workers +;$wk=3s0-8s0,3s0h-8s0h; 12 workers +[lua] +lpm4 = dofile("ipv4.lua") +user_table = dofile("user_table-65K-bng.lua") +[defaults] +mempool size=16K + +[global] +start time=20 +name=BNG (1Q) + +[core 0s0] +mode=master +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=nop +rx ring=yes +tx port=cpe0 +task=1 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp + +[core 2s0] +name=LB-inet +task=0 +mode=nop +rx ring=yes +tx port=inet0 +task=1 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 + +[core 3s0] +name=LB-cpe +task=0 +mode=nop +rx ring=yes +tx port=cpe1 +task=1 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp + +[core 4s0] +name=LB-inet +task=0 +mode=nop +rx ring=yes +tx port=inet1 +task=1 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx cores from routing table=2s0,4s0 +route table=lpm4 +local ipv4=21.22.23.24 +user table=user_table +handle arp=yes + +task=1 +mode=qinqencapv4 +rx ring=yes +user table=user_table +tx cores from cpe table=1s0,3s0 remap=cpe0,cpe1 diff --git a/VNFs/DPPD-PROX/config/bng-4ports.cfg b/VNFs/DPPD-PROX/config/bng-4ports.cfg new file mode 100644 index 00000000..6ef195ae --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-4ports.cfg @@ -0,0 +1,125 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a simplified Border Network Gateway (BNG) on the +; first socket (socket 0). Four load balancers (two physical cores, four logical +; cores) and eight workers (four physical cores, eight logical cores) are set +; up. The number of workers can be changed by uncommenting one of the lines in +; the [variables] section. If this configuration is to be used on a system with +; few cores, the number of workers need to be reduced. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=hardware +[port 1] +name=inet0 +mac=hardware +[port 2] +name=cpe1 +mac=hardware +[port 3] +name=inet1 +mac=hardware + +[lua] +lpm4 = dofile("ipv4.lua") +user_table = dofile("user_table-65K-bng.lua") +[variables] +;uncomment one of the following to change the number of workers +;$wk=3s0,3s0h; 2 workers +;$wk=3s0-4s0,3s0h-4s0h; 4 workers +;$wk=3s0-5s0,3s0h-5s0h; 6 workers +$wk=3s0-6s0,3s0h-6s0h; 8 workers +;$wk=3s0-7s0,3s0h-7s0h; 10 workers +;$wk=3s0-8s0,3s0h-8s0h; 12 workers + +[defaults] +mempool size=16K +qinq tag=0x0081 +[global] +start time=20 +name=BNG +[core 0s0] +mode=master +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0 proto=arp +drop=no + +[core 1s0h] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=no + +[core 2s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0 proto=arp +drop=no + +[core 2s0h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 +drop=no + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1 +route table=lpm4 +local ipv4=21.22.23.24 +handle arp=yes +user table=user_table +drop=no +fast path handle arp=yes + +task=1 +mode=qinqencapv4 +rx ring=yes ; gre received from internal queue +tx ports from cpe table=cpe0,cpe1 +user table=user_table +drop=no diff --git a/VNFs/DPPD-PROX/config/bng-8ports-17cores.cfg b/VNFs/DPPD-PROX/config/bng-8ports-17cores.cfg new file mode 100644 index 00000000..295aa131 --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-8ports-17cores.cfg @@ -0,0 +1,222 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-qos-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=6 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:01:00:00:01 +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +mac=00:00:01:00:00:04 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +mac=00:00:02:00:00:01 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 5] +name=inet2 +mac=00:00:02:00:00:02 +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +mac=00:00:02:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +mac=00:00:02:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[variables] +$wk=9s0-16s0,9s0h-16s0h +$lb_drop=no +$wt_drop=no +$rxd=256 +$txd=256 +$promiscuous=yes +$mcs=128 +$rs=1024 + +[defaults] +mempool size=16K +qinq tag=0xa888 + +[lua] +lpm4 = dofile("ipv4-4ports.lua") +user_table = dofile("user_table-131K-bng.lua") +[global] +start time=20 +name=BNG + QoS +unique mempool per socket=yes +mp rings=yes + +[core 0s0] +mode=master + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe2 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe3 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 5s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 6s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet1 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 7s0] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet2 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 8s0] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wt_drop +handle arp=yes +cpe table timeout ms=15000000 +ctrl path polling frequency=10000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx ports from cpe table=cpe0,cpe1,cpe2,cpe3 +drop=$wt_drop +ctrl path polling frequency=10000 +user table=user_table +ring size=$rs diff --git a/VNFs/DPPD-PROX/config/bng-8ports-25cores.cfg b/VNFs/DPPD-PROX/config/bng-8ports-25cores.cfg new file mode 100644 index 00000000..602e0a3c --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-8ports-25cores.cfg @@ -0,0 +1,222 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-qos-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=6 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:01:00:00:01 +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +mac=00:00:01:00:00:04 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +mac=00:00:02:00:00:01 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 5] +name=inet2 +mac=00:00:02:00:00:02 +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +mac=00:00:02:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +mac=00:00:02:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[variables] +$wk=9s0-24s0,9s0h-24s0h +$lb_drop=no +$wt_drop=no +$rxd=256 +$txd=256 +$promiscuous=yes +$mcs=128 +$rs=1024 + +[defaults] +mempool size=16K +qinq tag=0xa888 + +[lua] +lpm4 = dofile("ipv4-4ports.lua") +user_table = dofile("user_table-131K-bng.lua") +[global] +start time=20 +name=BNG + QoS +unique mempool per socket=yes +mp rings=yes + +[core 0s0] +mode=master + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe2 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s0] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe3 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 5s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 6s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet1 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 7s0] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet2 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 8s0] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wt_drop +handle arp=yes +cpe table timeout ms=15000000 +ctrl path polling frequency=10000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx ports from cpe table=cpe0,cpe1,cpe2,cpe3 +drop=$wt_drop +ctrl path polling frequency=10000 +user table=user_table +ring size=$rs diff --git a/VNFs/DPPD-PROX/config/bng-8ports.cfg b/VNFs/DPPD-PROX/config/bng-8ports.cfg new file mode 100644 index 00000000..07d31cbd --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-8ports.cfg @@ -0,0 +1,231 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[variables] +;uncomment one of the following to change the number of worker threads +$lb_drop=yes +$wk_drop=yes +$rxd=128 +$txd=128 +$promiscuous=yes +$mp=16K +$mcs=512 +$rs=128 + +[port 0] +name=cpe0 +mac=00:00:01:00:00:01 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +mac=00:00:01:00:00:03 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +mac=00:00:01:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +mac=00:00:01:00:00:01 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 5] +name=inet2 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +mac=00:00:01:00:00:03 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +mac=00:00:01:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous +[lua] +lpm4 = dofile("ipv4-4ports.lua") +dscp_table = dofile("dscp.lua") +user_table = dofile("user_table-131K-bng.lua") + +wk="5s1-9s1,5s1h-9s1h" +name="BNG (" .. task_count(wk) .. " workers)" + +[defaults] +mempool size=$mp +qinq tag=0xa888 + +[global] +start time=10 +duration time=0 +name=$name +unique mempool per socket=yes +shuffle=yes + +[core 0s1] +task=0 +mode=master +tx cores=(${wk})t0m + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s1] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 1s1h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s1] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet2 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s1h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s1] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s1h] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s1] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe2 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s1h] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe3 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +;***************************************************************************************** +;#### Worker Threads receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=WK +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wk_drop +handle arp=yes +cpe table timeout ms=15000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes ; gre received from internal queue +tx ports from cpe table=cpe0,cpe1,cpe2,cpe3 +drop=$wk_drop +user table=user_table diff --git a/VNFs/DPPD-PROX/config/bng-no-cpu-topology-4ports.cfg b/VNFs/DPPD-PROX/config/bng-no-cpu-topology-4ports.cfg new file mode 100644 index 00000000..02598098 --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-no-cpu-topology-4ports.cfg @@ -0,0 +1,102 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:03 +[port 2] +name=cpe1 +mac=00:00:00:00:00:02 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 +[variables] +$wk=3-6,9-12 + +[defaults] +mempool size=16K +[lua] +lpm4 = dofile("ipv4.lua") +user_table = dofile("user_table-65K-bng.lua") +[global] +start time=20 +name=vBNG + +[core 0] +mode=master +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp + +[core 7] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 + +[core 2] +name=LB-cpe +task=0 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp + +[core 8] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1 +route table=lpm4 +local ipv4=21.22.23.24 +handle arp=yes +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx ports from cpe table=cpe0,cpe1 +user table=user_table diff --git a/VNFs/DPPD-PROX/config/bng-ovs-usv-4ports.cfg b/VNFs/DPPD-PROX/config/bng-ovs-usv-4ports.cfg new file mode 100644 index 00000000..48321c0d --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-ovs-usv-4ports.cfg @@ -0,0 +1,137 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration is provided for virtualized environments running on top of +; a soft-switch. Specifically, ingredients are Open vSwitch (openvswitch.org) +; and Qemu version 1.6.2. Note that since the currently supported version of +; Open vSwitch does not handle all the protocols that are used in the full BNG, +; PROX has to be recompiled to use different packet processing paths as a +; workaround. DPDK version 1.8.0 should be used with this configuration and it +; has to be compiled with COMBINE_LIBS enabled: +; make install T=$RTE_TARGET CONFIG_RTE_BUILD_COMBINE_LIBS=y CONFIG_RTE_LIBRTE_VHOST=y +; The following commands demonstrate how to set up Open vSwitch: +; git clone https://github.com/openvswitch/ovs.git +; cd ovs +; git checkout 5c62a855c7bb24424cbe7ec48ecf2f128db8b102 +; ./boot.sh && ./configure --with-dpdk=$RTE_SDK/$RTE_TARGET --disable-ssl && make +; This configuration is intended to be used in a VM with 4 virtual ports. This +; means that 4 virtual ports (with type dpdkvhost) and 4 physical ports (with +; type dpdk) will need to be added and connected through open-flow commands in +; Open vSwitch. After Open vSwitch has been set up on the host, PROX needs to be +; recompiled in the VM as follows before running it with this configuration: +; make BNG_QINQ=n MPLS_ROUTING=n +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:03 +[port 2] +name=cpe1 +mac=00:00:00:00:00:02 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 +[variables] +$wk=5s0,6s0 + +[defaults] +mempool size=16K +[lua] +lpm4 = dofile("ipv4.lua") +user_table =dofile("user_table-65K-bng.lua") +[global] +start time=20 +name=BNG (OVS) + +[core 0s0] +mode=master +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=nop +rx ring=yes +tx port=cpe0 +task=1 +mode=lbqinq +rx port=cpe0 +tx cores=(${wk})t0 proto=ipv4 + +[core 2s0] +name=LB-inet +task=0 +mode=nop +rx ring=yes +tx port=inet0 +task=1 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 + +[core 3s0] +name=LB-cpe +task=0 +mode=nop +rx ring=yes +tx port=cpe1 +task=1 +mode=lbqinq +rx port=cpe1 +tx cores=(${wk})t0 proto=ipv4 + +[core 4s0] +name=LB-inet +task=0 +mode=nop +rx ring=yes +tx port=inet1 +task=1 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: Upstream traffic +;#### Task 1: Downstream traffic +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx cores from routing table=2s0,4s0 +route table=lpm4 +local ipv4=21.22.23.24 +handle arp=no +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx cores from cpe table=1s0,3s0 remap=cpe0,cpe1 +user table=user_table diff --git a/VNFs/DPPD-PROX/config/bng-qos-4ports.cfg b/VNFs/DPPD-PROX/config/bng-qos-4ports.cfg new file mode 100644 index 00000000..cd26fd5b --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-qos-4ports.cfg @@ -0,0 +1,209 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; Compared to config/bng-4ports.cfg, this configuration sets up a BNG with QoS +; functionality. In total, an extra eight cores (four physical cores) are needed +; to run this configuration. Four cores are used for QoS, two cores are assigned +; with the task of classifying upstream packets and two cores are assigned with +; transmitting downstream packets. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=hardware +[port 1] +name=inet0 +mac=hardware +[port 2] +name=cpe1 +mac=hardware +[port 3] +name=inet1 +mac=hardware +[variables] +;uncomment one of the following to change the number of workers +;$wk=7s0,7s0h; 2 workers +;$wk=7s0-8s0,7s0h-8s0h; 4 workers +$wk=7s0-9s0,7s0h-9s0h; 6 workers +;$wk=7s0-10s0,7s0h-10s0h; 8 workers + +[defaults] +mempool size=128K +qinq tag=0xa888;0x0081 +[lua] +lpm4 = dofile("ipv4.lua") +dscp_table = dofile("dscp.lua") +user_table = dofile("user_table-65K-bng.lua") +[global] +start time=20 +name=BNG + QoS + +[core 0s0] +mode=master +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=no + +[core 1s0h] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 + +[core 2s0] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=no + +[core 2s0h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1 +route table=lpm4 +local ipv4=21.22.23.24 +handle arp=yes +drop=no +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx cores from cpe table=3s0,4s0 remap=cpe0,cpe1 ;map packets going to cpe0 to 3s0 and cpe1 to 4s0 +classify=yes +dscp=dscp_table +user table=user_table + +;***************************************************************************************** +;#### Downstream QoS receiving from workers +;#### classification done by workers +;#### Downstream QoS = QoS core and TX core +[core 3s0] +name=txqos0 +task=0 +mode=qos +rx ring=yes +tx cores=3s0ht0 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 3s0h] +name=txnop0 +task=0 +mode=nop +rx ring=yes +tx port=cpe0 +drop=no + +[core 4s0] +name=txqos1 +task=0 +mode=qos +rx ring=yes +tx cores=4s0ht0 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 4s0h] +name=txnop1 +task=0 +mode=nop +rx ring=yes +tx port=cpe1 +drop=no + +;***************************************************************************************** +;#### upstream QoS receiving from CPE +;#### classification done by RX, QoS core +;#### upstream QoS = RX core (classify) + QoS core +[core 5s0h] +name=rxcl0 +task=0 +mode=classify +rx port=cpe0 +tx cores=5s0t0 +dscp=dscp_table +drop=no +user table=user_table + +[core 5s0] +name=rxqos0 +task=0 +mode=qos +rx ring=yes +tx cores=1s0t0 +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table + +[core 6s0h] +name=rxcl1 +task=0 +mode=classify +rx port=cpe1 +tx cores=6s0t0 +dscp=dscp_table +drop=no +user table=user_table + +[core 6s0] +name=rxqos1 +task=0 +mode=qos +rx ring=yes +tx cores=2s0t0 +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table diff --git a/VNFs/DPPD-PROX/config/bng-qos-8ports.cfg b/VNFs/DPPD-PROX/config/bng-qos-8ports.cfg new file mode 100644 index 00000000..c191a22e --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-qos-8ports.cfg @@ -0,0 +1,323 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-qos-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +;mac=00:00:01:00:00:01 +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +;mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +;mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +;mac=00:00:01:00:00:04 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +;mac=00:00:01:00:00:01 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 5] +name=inet2 +;mac=00:00:01:00:00:02 +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +;mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +;mac=00:00:01:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[variables] +$wk=9s1,5s1h-9s1h; 6 workers +$lb_drop=no +$wt_drop=no +$rxd=256 +$txd=256 +$promiscuous=yes +$mp=6K +$mcs=128 +$rs=256 + +[defaults] +mempool size=128K +qinq tag=0xa888 + +[lua] +lpm4 = dofile("ipv4-4ports.lua") +dscp_table = dofile("dscp.lua") +user_table = dofile("user_table-131K-bng.lua") +[global] +start time=20 +name=BNG + QoS +unique mempool per socket=no + +[core 0s1] +mode=master + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s1] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +task=1 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 1s1h] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +task=1 +mode=lbnetwork +rx port=inet2 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s1] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +task=1 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s1h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +task=1 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wt_drop +handle arp=yes +cpe table timeout ms=15000000 +ctrl path polling frequency=10000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx cores from cpe table=3s1,3s1h,4s1,4s1h remap=cpe0,cpe1,cpe2,cpe3 ;map packets going to cpe0 to 3s1 and cpe1 to 4s1 +classify=yes +drop=$wt_drop +ctrl path polling frequency=10000 +user table=user_table +dscp=dscp_table + +;***************************************************************************************** +;#### Downstream QoS receiving from workers +;#### classification done by workers +;#### Downstream QoS = QoS core and TX core +[core 3s1] +name=txqos0 +task=0 +mode=qos +rx ring=yes +tx port=cpe0 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 3s1h] +name=txqos1 +task=0 +mode=qos +rx ring=yes +tx port=cpe1 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 4s1] +name=txqos2 +task=0 +mode=qos +rx ring=yes +tx port=cpe2 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 4s1h] +name=txqos3 +task=0 +mode=qos +rx ring=yes +drop=no +tx port=cpe3 +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +;***************************************************************************************** +;#### upstream QoS receiving from CPE +;#### classification done by RX, QoS core +;#### upstream QoS = RX core (classify) + QoS core +[core 5s1] +name=rxqos0 +task=0 +mode=qos +rx port=cpe0 +tx cores=1s1t0 +classify=yes +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 6s1] +name=rxqos1 +task=0 +mode=qos +rx port=cpe1 +classify=yes +dscp=dscp_table +tx cores=1s1t1 +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 7s1] +name=rxqos1 +task=0 +mode=qos +rx port=cpe2 +tx cores=2s1t0 +classify=yes +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 8s1] +task=0 +mode=qos +rx port=cpe3 +tx cores=2s1t1 +classify=yes +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table diff --git a/VNFs/DPPD-PROX/config/bng-qos-8ports_17cores.cfg b/VNFs/DPPD-PROX/config/bng-qos-8ports_17cores.cfg new file mode 100644 index 00000000..74c45872 --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-qos-8ports_17cores.cfg @@ -0,0 +1,411 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-qos-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=6 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:01:00:00:01 +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +mac=00:00:01:00:00:04 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +mac=00:00:02:00:00:01 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 5] +name=inet2 +mac=00:00:02:00:00:02 +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +mac=00:00:02:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +mac=00:00:02:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[variables] +$wk=0s0h,13s0-16s0,13s0h-16s0h,4s0,4s0h +$lb_drop=no +$wt_drop=no +$rxd=256 +$txd=256 +$promiscuous=yes +$mcs=128 +$rs=1024 + +[defaults] +mempool size=256K +qinq tag=0xa888 + +[lua] +lpm4 = dofile("ipv4-4ports.lua") +dscp_table = dofile("dscp.lua") +user_table = dofile("user_table-131K-bng.lua") +[global] +start time=20 +name=BNG + QoS +unique mempool per socket=no +mp rings=yes +enable bypass=yes + +[core 0s0] +mode=master + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +name=LB-cpe +task=1 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 1s0h] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +name=LB-cpe +task=1 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +name=LB-inet +task=1 +mode=lbnetwork +rx port=inet1 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet2 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +name=LB-inet +task=1 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s0] +name=classify1 +task=0 +mode=classify +rx port=cpe0 +tx cores=9s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +name=classify2 +task=1 +mode=classify +rx port=cpe1 +tx cores=10s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 3s0h] +name=classify3 +task=0 +mode=classify +rx port=cpe2 +tx cores=11s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +name=classify4 +task=1 +mode=classify +rx port=cpe3 +tx cores=12s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wt_drop +handle arp=yes +cpe table timeout ms=15000000 +ctrl path polling frequency=10000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx cores from cpe table=5s0t1,6s0t1,7s0t1,8s0t1 remap=cpe0,cpe1,cpe2,cpe3 ;map packets going to cpe0 to 3s0 and cpe1 to 4s0 +classify=yes +drop=$wt_drop +ctrl path polling frequency=10000 +user table=user_table +dscp=dscp_table +ring size=$rs + +;***************************************************************************************** +;#### Downstream QoS receiving from workers +;#### classification done by workers +;#### Downstream QoS = QoS core and TX core +[core 5s0] +name=txqos0 +task=0 +mode=nop +rx ring=yes +tx port=cpe0 +drop=no + +task=1 +mode=qos +rx ring=yes +tx cores=5s0t0 +drop=yes +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 6s0] +name=txqos1 +task=0 +mode=nop +rx ring=yes +tx port=cpe1 +drop=no + +task=1 +mode=qos +rx ring=yes +;tx port=cpe1 +tx cores=6s0t0 +drop=yes +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 7s0] +name=txqos2 +task=0 +mode=nop +rx ring=yes +tx port=cpe2 +drop=no + +task=1 +mode=qos +rx ring=yes +;tx port=cpe2 +tx cores=7s0t0 +drop=yes +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 8s0] +name=txqos3 +task=0 +mode=nop +rx ring=yes +tx port=cpe3 +drop=no + +task=1 +mode=qos +rx ring=yes +drop=yes +;tx port=cpe3 +tx cores=8s0t0 +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +;***************************************************************************************** +;#### upstream QoS receiving from CPE +;#### classification done by RX, QoS core +;#### upstream QoS = RX core (classify) + QoS core +[core 9s0] +name=rxqos0 +task=0 +mode=qos +;rx port=cpe0 +rx ring=yes +tx cores=1s0 +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 10s0] +name=rxqos0 +task=0 +mode=qos +;rx port=cpe1 +rx ring=yes +;classify=yes +;dscp=dscp_table +tx cores=1s0t1 +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 11s0] +name=rxqos2 +task=0 +mode=qos +;rx port=cpe2 +rx ring=yes +tx cores=1s0h +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 12s0] +name=rxqos3 +task=0 +mode=qos +;rx port=cpe3 +rx ring=yes +tx cores=1s0ht1 +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table diff --git a/VNFs/DPPD-PROX/config/bng-qos-8ports_25cores.cfg b/VNFs/DPPD-PROX/config/bng-qos-8ports_25cores.cfg new file mode 100644 index 00000000..cfb58bac --- /dev/null +++ b/VNFs/DPPD-PROX/config/bng-qos-8ports_25cores.cfg @@ -0,0 +1,438 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration sets up a system that handles the same workload as +; config/bng-qos-4ports.cfg, but on 8 ports instead of 4 and on CPU socket 1 +; instead of socket 0. +;; + +[eal options] +-n=6 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:01:00:00:01 +tx desc=$txd +promiscuous=$promiscuous + +[port 1] +name=inet0 +mac=00:00:01:00:00:02 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[port 2] +name=cpe1 +mac=00:00:01:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 3] +name=inet1 +mac=00:00:01:00:00:04 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 4] +name=cpe2 +mac=00:00:02:00:00:01 +tx desc=$txd +rx desc=$rxd +promiscuous=$promiscuous + +[port 5] +name=inet2 +mac=00:00:02:00:00:02 +tx desc=$txd +promiscuous=$promiscuous + +[port 6] +name=cpe3 +mac=00:00:02:00:00:03 +tx desc=$txd +promiscuous=$promiscuous + +[port 7] +name=inet3 +mac=00:00:02:00:00:04 +rx desc=$rxd +tx desc=$txd +promiscuous=$promiscuous + +[variables] +$wk=0s0h,15s0-20s0,7s0h-20s0h +$lb_drop=no +$wt_drop=no +$rxd=256 +$txd=256 +$promiscuous=yes +$mcs=128 +$rs=1024 +$tx1=21s0 +$tx2=22s0 +$tx3=23s0 +$tx4=24s0 + +[defaults] +mempool size=256K +qinq tag=0xa888 + +[lua] +lpm4 = dofile("ipv4-4ports.lua") +dscp_table = dofile("dscp.lua") +user_table = dofile("user_table-131K-bng.lua") +[global] +start time=20 +name=BNG + QoS +unique mempool per socket=yes +mp rings=yes +enable bypass=yes + +[core 0s0] +mode=master + +; IPv4 +;***************************************************************************************** +;##### Load Balancing receiving from CPE and from Internet #### +[core 1s0] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 1s0h] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 2s0h] +name=LB-cpe +task=0 +mode=lbqinq +rx ring=yes +tx cores=(${wk})t0 proto=ipv4 +tx cores=(${wk})t0p proto=arp +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s0] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 3s0h] +name=LB-inet +task=0 +mode=lbnetwork +rx port=inet1 +untag mpls=yes +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s0] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet2 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 4s0h] +name=LB-inet +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wk})t1 proto=ipv4 +drop=$lb_drop +memcache size=$mcs +ring size=$rs + +[core 5s0] +name=classify1 +task=0 +mode=classify +rx port=cpe0 +tx cores=11s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table +memcache size=$mcs + +[core 5s0h] +name=classify2 +task=0 +mode=classify +rx port=cpe1 +tx cores=12s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table +memcache size=$mcs + +[core 6s0] +name=classify3 +task=0 +mode=classify +rx port=cpe2 +tx cores=13s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table +memcache size=$mcs + +[core 6s0h] +name=classify4 +task=0 +mode=classify +rx port=cpe3 +tx cores=14s0 +dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table +memcache size=$mcs + +;***************************************************************************************** +;#### Workers receiving from LB +;#### Task 0: QinQ decapsulation + gre encapsulation + routing +;#### Task 1: ARP +;#### Task 2: GRE depcapsulation + QinQ encapsulation + use learned mac +[core $wk] +name=Worker +task=0 +mode=qinqdecapv4 +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +local ipv4=21.22.23.24 +drop=$wt_drop +handle arp=yes +cpe table timeout ms=15000000 +ctrl path polling frequency=10000 +user table=user_table + +task=1 +mode=qinqencapv4 +rx ring=yes +tx cores from cpe table=7s0,8s0,9s0,10s0 remap=cpe0,cpe1,cpe2,cpe3 ;map packets going to cpe0 to 3s0 and cpe1 to 4s0 +classify=yes +drop=$wt_drop +ctrl path polling frequency=10000 +user table=user_table +dscp=dscp_table +ring size=$rs + +;***************************************************************************************** +;#### Downstream QoS receiving from workers +;#### classification done by workers +;#### Downstream QoS = QoS core and TX core +[core 7s0] +name=txqos0 +task=0 +mode=qos +rx ring=yes +;tx port=cpe0 +tx cores=$tx1 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 8s0] +name=txqos0 +task=0 +mode=qos +rx ring=yes +;tx port=cpe1 +tx cores=$tx2 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 9s0] +name=txqos2 +task=0 +mode=qos +rx ring=yes +;tx port=cpe2 +tx cores=$tx3 +drop=no +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +[core 10s0] +name=txqos3 +task=0 +mode=qos +rx ring=yes +drop=no +;tx port=cpe3 +tx cores=$tx4 +pipe tc rate=125000 +pipe tb rate=125000 +user table=user_table + +;***************************************************************************************** +;#### upstream QoS receiving from CPE +;#### classification done by RX, QoS core +;#### upstream QoS = RX core (classify) + QoS core +[core 11s0] +name=rxqos0 +task=0 +mode=qos +;rx port=cpe0 +rx ring=yes +tx cores=1s0 +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 12s0] +name=rxqos0 +task=0 +mode=qos +;rx port=cpe1 +rx ring=yes +;classify=yes +;dscp=dscp_table +tx cores=1s0h +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 13s0] +name=rxqos2 +task=0 +mode=qos +;rx port=cpe2 +rx ring=yes +tx cores=2s0 +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core 14s0] +name=rxqos3 +task=0 +mode=qos +;rx port=cpe3 +rx ring=yes +tx cores=2s0h +;classify=yes +;dscp=dscp_table +pipe tc rate=125000 +pipe tb rate=125000 +drop=no +user table=user_table +dscp=dscp_table + +[core $tx1] +name=tx1 +task=0 +mode=read +rx ring=yes +tx port=cpe0 +ring size=$rs +drop=no + +[core $tx2] +name=tx2 +task=0 +mode=read +rx ring=yes +tx port=cpe1 +ring size=$rs +drop=no + +[core $tx3] +name=tx3 +task=0 +mode=read +rx ring=yes +tx port=cpe2 +ring size=$rs +drop=no + +[core $tx4] +name=tx4 +task=0 +mode=read +rx ring=yes +tx port=cpe3 +ring size=$rs +drop=no diff --git a/VNFs/DPPD-PROX/config/cgnat.cfg b/VNFs/DPPD-PROX/config/cgnat.cfg new file mode 100644 index 00000000..4015d3ab --- /dev/null +++ b/VNFs/DPPD-PROX/config/cgnat.cfg @@ -0,0 +1,58 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=if1 +mac=hardware + +[lua] +nat_table = dofile("cgnat_table.lua") +lpm4 = dofile("ipv4_1port.lua") + +[defaults] +mempool size=4K + +[global] +start time=5 +name=CGNAT + +[core 0s0] +mode=master + +[core 1s0] +name=nat +task=0 +mode=cgnat +private=yes +nat table=nat_table +route table=lpm4 +rx port=if0 +tx ports from routing table=if1 + +task=1 +mode=cgnat +private=no +nat table=nat_table +route table=lpm4 +rx port=if1 +tx port=if0 diff --git a/VNFs/DPPD-PROX/config/cgnat_table.lua b/VNFs/DPPD-PROX/config/cgnat_table.lua new file mode 100644 index 00000000..26ae9c7a --- /dev/null +++ b/VNFs/DPPD-PROX/config/cgnat_table.lua @@ -0,0 +1,38 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local cgnat = {} +cgnat.dynamic = { + {public_ip_range_start = ip("20.0.1.0"),public_ip_range_stop = ip("20.0.1.15"), public_port = val_range(0,65535)}, + {public_ip_range_start = ip("20.0.1.16"),public_ip_range_stop = ip("20.0.1.31"), public_port = val_range(0,65535)}, +} +cgnat.static_ip_port = { + {src_ip = ip("192.168.2.1"), src_port = 68, dst_ip = ip("20.0.2.1"), dst_port = 68}, + {src_ip = ip("192.168.2.1"), src_port = 168, dst_ip = ip("20.0.2.1"), dst_port = 5000}, + {src_ip = ip("192.168.2.1"), src_port = 268, dst_ip = ip("20.0.2.1"), dst_port = 5001}, + {src_ip = ip("192.168.2.1"), src_port = 368, dst_ip = ip("20.0.2.1"), dst_port = 5002}, +} +cgnat.static_ip = { + {src_ip = ip("192.168.3.1"), dst_ip = ip("20.0.3.1")}, + {src_ip = ip("192.168.3.2"), dst_ip = ip("20.0.3.2")}, + {src_ip = ip("192.168.3.3"), dst_ip = ip("20.0.3.3")}, + {src_ip = ip("192.168.3.4"), dst_ip = ip("20.0.3.4")}, + {src_ip = ip("192.168.3.5"), dst_ip = ip("20.0.3.5")}, + {src_ip = ip("192.168.3.6"), dst_ip = ip("20.0.3.6")}, + {src_ip = ip("192.168.3.7"), dst_ip = ip("20.0.3.7")}, + {src_ip = ip("192.168.3.8"), dst_ip = ip("20.0.3.8")}, +} +return cgnat diff --git a/VNFs/DPPD-PROX/config/cpe_table.lua b/VNFs/DPPD-PROX/config/cpe_table.lua new file mode 100644 index 00000000..305e14f2 --- /dev/null +++ b/VNFs/DPPD-PROX/config/cpe_table.lua @@ -0,0 +1,2066 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +return { + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=0, cidr = cidr("192.168.0.0/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=0, cidr = cidr("192.168.0.8/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=1, cidr = cidr("192.168.0.16/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=1, cidr = cidr("192.168.0.24/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=2, cidr = cidr("192.168.0.32/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=2, cidr = cidr("192.168.0.40/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=3, cidr = cidr("192.168.0.48/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=3, cidr = cidr("192.168.0.56/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=4, cidr = cidr("192.168.0.64/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=4, cidr = cidr("192.168.0.72/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=5, cidr = cidr("192.168.0.80/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=5, cidr = cidr("192.168.0.88/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=6, cidr = cidr("192.168.0.96/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=6, cidr = cidr("192.168.0.104/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=7, cidr = cidr("192.168.0.112/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=7, cidr = cidr("192.168.0.120/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=8, cidr = cidr("192.168.0.128/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=8, cidr = cidr("192.168.0.136/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=9, cidr = cidr("192.168.0.144/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=9, cidr = cidr("192.168.0.152/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=10, cidr = cidr("192.168.0.160/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=10, cidr = cidr("192.168.0.168/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=11, cidr = cidr("192.168.0.176/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=11, cidr = cidr("192.168.0.184/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=12, cidr = cidr("192.168.0.192/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=12, cidr = cidr("192.168.0.200/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=13, cidr = cidr("192.168.0.208/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=13, cidr = cidr("192.168.0.216/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=14, cidr = cidr("192.168.0.224/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=14, cidr = cidr("192.168.0.232/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=15, cidr = cidr("192.168.0.240/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=15, cidr = cidr("192.168.0.248/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=16, cidr = cidr("192.168.1.0/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=16, cidr = cidr("192.168.1.8/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=17, cidr = cidr("192.168.1.16/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=17, cidr = cidr("192.168.1.24/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=18, cidr = cidr("192.168.1.32/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=18, cidr = cidr("192.168.1.40/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=19, cidr = cidr("192.168.1.48/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=19, cidr = cidr("192.168.1.56/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=20, cidr = cidr("192.168.1.64/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=20, cidr = cidr("192.168.1.72/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=21, cidr = cidr("192.168.1.80/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=21, cidr = cidr("192.168.1.88/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=22, cidr = cidr("192.168.1.96/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=22, cidr = cidr("192.168.1.104/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=23, cidr = cidr("192.168.1.112/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=23, cidr = cidr("192.168.1.120/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=24, cidr = cidr("192.168.1.128/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=24, cidr = cidr("192.168.1.136/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=25, cidr = cidr("192.168.1.144/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=25, cidr = cidr("192.168.1.152/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=26, cidr = cidr("192.168.1.160/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=26, cidr = cidr("192.168.1.168/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=27, cidr = cidr("192.168.1.176/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=27, cidr = cidr("192.168.1.184/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=28, cidr = cidr("192.168.1.192/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=28, cidr = cidr("192.168.1.200/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=29, cidr = cidr("192.168.1.208/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=29, cidr = cidr("192.168.1.216/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=30, cidr = cidr("192.168.1.224/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=30, cidr = cidr("192.168.1.232/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=31, cidr = cidr("192.168.1.240/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=31, cidr = cidr("192.168.1.248/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=32, cidr = cidr("192.168.2.0/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=32, cidr = cidr("192.168.2.8/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=33, cidr = cidr("192.168.2.16/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=33, cidr = cidr("192.168.2.24/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=34, cidr = cidr("192.168.2.32/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=34, cidr = cidr("192.168.2.40/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=35, cidr = cidr("192.168.2.48/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=35, cidr = cidr("192.168.2.56/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=36, cidr = cidr("192.168.2.64/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=36, cidr = cidr("192.168.2.72/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=37, cidr = cidr("192.168.2.80/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=37, cidr = cidr("192.168.2.88/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=38, cidr = cidr("192.168.2.96/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=38, cidr = cidr("192.168.2.104/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=39, cidr = cidr("192.168.2.112/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=39, cidr = cidr("192.168.2.120/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=40, cidr = cidr("192.168.2.128/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=40, cidr = cidr("192.168.2.136/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=41, cidr = cidr("192.168.2.144/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=41, cidr = cidr("192.168.2.152/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=42, cidr = cidr("192.168.2.160/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=42, cidr = cidr("192.168.2.168/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=43, cidr = cidr("192.168.2.176/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=43, cidr = cidr("192.168.2.184/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=44, cidr = cidr("192.168.2.192/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=44, cidr = cidr("192.168.2.200/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=45, cidr = cidr("192.168.2.208/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=45, cidr = cidr("192.168.2.216/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=46, cidr = cidr("192.168.2.224/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=46, cidr = cidr("192.168.2.232/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=47, cidr = cidr("192.168.2.240/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=47, cidr = cidr("192.168.2.248/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=48, cidr = cidr("192.168.3.0/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=48, cidr = cidr("192.168.3.8/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=49, cidr = cidr("192.168.3.16/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=49, cidr = cidr("192.168.3.24/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=50, cidr = cidr("192.168.3.32/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=50, cidr = cidr("192.168.3.40/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=51, cidr = cidr("192.168.3.48/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=51, cidr = cidr("192.168.3.56/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=52, cidr = cidr("192.168.3.64/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=52, cidr = cidr("192.168.3.72/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=53, cidr = cidr("192.168.3.80/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=53, cidr = cidr("192.168.3.88/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=54, cidr = cidr("192.168.3.96/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=54, cidr = cidr("192.168.3.104/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=55, cidr = cidr("192.168.3.112/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=55, cidr = cidr("192.168.3.120/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=56, cidr = cidr("192.168.3.128/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=56, cidr = cidr("192.168.3.136/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=57, cidr = cidr("192.168.3.144/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=57, cidr = cidr("192.168.3.152/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=58, cidr = cidr("192.168.3.160/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=58, cidr = cidr("192.168.3.168/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=59, cidr = cidr("192.168.3.176/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=59, cidr = cidr("192.168.3.184/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=60, cidr = cidr("192.168.3.192/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=60, cidr = cidr("192.168.3.200/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=61, cidr = cidr("192.168.3.208/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=61, cidr = cidr("192.168.3.216/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=62, cidr = cidr("192.168.3.224/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=62, cidr = cidr("192.168.3.232/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=63, cidr = cidr("192.168.3.240/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=63, cidr = cidr("192.168.3.248/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=64, cidr = cidr("192.168.4.0/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=64, cidr = cidr("192.168.4.8/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=65, cidr = cidr("192.168.4.16/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=65, cidr = cidr("192.168.4.24/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=66, cidr = cidr("192.168.4.32/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=66, cidr = cidr("192.168.4.40/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=67, cidr = cidr("192.168.4.48/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=67, cidr = cidr("192.168.4.56/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=68, cidr = cidr("192.168.4.64/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=68, cidr = cidr("192.168.4.72/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=69, cidr = cidr("192.168.4.80/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=69, cidr = cidr("192.168.4.88/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=70, cidr = cidr("192.168.4.96/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=70, cidr = cidr("192.168.4.104/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=71, cidr = cidr("192.168.4.112/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=71, cidr = cidr("192.168.4.120/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=72, cidr = cidr("192.168.4.128/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=72, cidr = cidr("192.168.4.136/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=73, cidr = cidr("192.168.4.144/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=73, cidr = cidr("192.168.4.152/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=74, cidr = cidr("192.168.4.160/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=74, cidr = cidr("192.168.4.168/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=75, cidr = cidr("192.168.4.176/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=75, cidr = cidr("192.168.4.184/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=76, cidr = cidr("192.168.4.192/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=76, cidr = cidr("192.168.4.200/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=77, cidr = cidr("192.168.4.208/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=77, cidr = cidr("192.168.4.216/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=78, cidr = cidr("192.168.4.224/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=78, cidr = cidr("192.168.4.232/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=79, cidr = cidr("192.168.4.240/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=79, cidr = cidr("192.168.4.248/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=80, cidr = cidr("192.168.5.0/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=80, cidr = cidr("192.168.5.8/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=81, cidr = cidr("192.168.5.16/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=81, cidr = cidr("192.168.5.24/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=82, cidr = cidr("192.168.5.32/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=82, cidr = cidr("192.168.5.40/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=83, cidr = cidr("192.168.5.48/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=83, cidr = cidr("192.168.5.56/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=84, cidr = cidr("192.168.5.64/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=84, cidr = cidr("192.168.5.72/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=85, cidr = cidr("192.168.5.80/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=85, cidr = cidr("192.168.5.88/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=86, cidr = cidr("192.168.5.96/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=86, cidr = cidr("192.168.5.104/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=87, cidr = cidr("192.168.5.112/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=87, cidr = cidr("192.168.5.120/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=88, cidr = cidr("192.168.5.128/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=88, cidr = cidr("192.168.5.136/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=89, cidr = cidr("192.168.5.144/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=89, cidr = cidr("192.168.5.152/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=90, cidr = cidr("192.168.5.160/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=90, cidr = cidr("192.168.5.168/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=91, cidr = cidr("192.168.5.176/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=91, cidr = cidr("192.168.5.184/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=92, cidr = cidr("192.168.5.192/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=92, cidr = cidr("192.168.5.200/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=93, cidr = cidr("192.168.5.208/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=93, cidr = cidr("192.168.5.216/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=94, cidr = cidr("192.168.5.224/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=94, cidr = cidr("192.168.5.232/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=95, cidr = cidr("192.168.5.240/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=95, cidr = cidr("192.168.5.248/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=96, cidr = cidr("192.168.6.0/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=96, cidr = cidr("192.168.6.8/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=97, cidr = cidr("192.168.6.16/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=97, cidr = cidr("192.168.6.24/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=98, cidr = cidr("192.168.6.32/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=98, cidr = cidr("192.168.6.40/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=99, cidr = cidr("192.168.6.48/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=99, cidr = cidr("192.168.6.56/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=100, cidr = cidr("192.168.6.64/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=100, cidr = cidr("192.168.6.72/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=101, cidr = cidr("192.168.6.80/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=101, cidr = cidr("192.168.6.88/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=102, cidr = cidr("192.168.6.96/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=102, cidr = cidr("192.168.6.104/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=103, cidr = cidr("192.168.6.112/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=103, cidr = cidr("192.168.6.120/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=104, cidr = cidr("192.168.6.128/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=104, cidr = cidr("192.168.6.136/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=105, cidr = cidr("192.168.6.144/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=105, cidr = cidr("192.168.6.152/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=106, cidr = cidr("192.168.6.160/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=106, cidr = cidr("192.168.6.168/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=107, cidr = cidr("192.168.6.176/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=107, cidr = cidr("192.168.6.184/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=108, cidr = cidr("192.168.6.192/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=108, cidr = cidr("192.168.6.200/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=109, cidr = cidr("192.168.6.208/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=109, cidr = cidr("192.168.6.216/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=110, cidr = cidr("192.168.6.224/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=110, cidr = cidr("192.168.6.232/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=111, cidr = cidr("192.168.6.240/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=111, cidr = cidr("192.168.6.248/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=112, cidr = cidr("192.168.7.0/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=112, cidr = cidr("192.168.7.8/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=113, cidr = cidr("192.168.7.16/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=113, cidr = cidr("192.168.7.24/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=114, cidr = cidr("192.168.7.32/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=114, cidr = cidr("192.168.7.40/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=115, cidr = cidr("192.168.7.48/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=115, cidr = cidr("192.168.7.56/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=116, cidr = cidr("192.168.7.64/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=116, cidr = cidr("192.168.7.72/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=117, cidr = cidr("192.168.7.80/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=117, cidr = cidr("192.168.7.88/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=118, cidr = cidr("192.168.7.96/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=118, cidr = cidr("192.168.7.104/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=119, cidr = cidr("192.168.7.112/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=119, cidr = cidr("192.168.7.120/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=120, cidr = cidr("192.168.7.128/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=120, cidr = cidr("192.168.7.136/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=121, cidr = cidr("192.168.7.144/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=121, cidr = cidr("192.168.7.152/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=122, cidr = cidr("192.168.7.160/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=122, cidr = cidr("192.168.7.168/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=123, cidr = cidr("192.168.7.176/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=123, cidr = cidr("192.168.7.184/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=124, cidr = cidr("192.168.7.192/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=124, cidr = cidr("192.168.7.200/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=125, cidr = cidr("192.168.7.208/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=125, cidr = cidr("192.168.7.216/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=126, cidr = cidr("192.168.7.224/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=126, cidr = cidr("192.168.7.232/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=127, cidr = cidr("192.168.7.240/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=127, cidr = cidr("192.168.7.248/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=128, cidr = cidr("192.168.8.0/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=128, cidr = cidr("192.168.8.8/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=129, cidr = cidr("192.168.8.16/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=129, cidr = cidr("192.168.8.24/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=130, cidr = cidr("192.168.8.32/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=130, cidr = cidr("192.168.8.40/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=131, cidr = cidr("192.168.8.48/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=131, cidr = cidr("192.168.8.56/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=132, cidr = cidr("192.168.8.64/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=132, cidr = cidr("192.168.8.72/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=133, cidr = cidr("192.168.8.80/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=133, cidr = cidr("192.168.8.88/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=134, cidr = cidr("192.168.8.96/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=134, cidr = cidr("192.168.8.104/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=135, cidr = cidr("192.168.8.112/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=135, cidr = cidr("192.168.8.120/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=136, cidr = cidr("192.168.8.128/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=136, cidr = cidr("192.168.8.136/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=137, cidr = cidr("192.168.8.144/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=137, cidr = cidr("192.168.8.152/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=138, cidr = cidr("192.168.8.160/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=138, cidr = cidr("192.168.8.168/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=139, cidr = cidr("192.168.8.176/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=139, cidr = cidr("192.168.8.184/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=140, cidr = cidr("192.168.8.192/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=140, cidr = cidr("192.168.8.200/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=141, cidr = cidr("192.168.8.208/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=141, cidr = cidr("192.168.8.216/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=142, cidr = cidr("192.168.8.224/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=142, cidr = cidr("192.168.8.232/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=143, cidr = cidr("192.168.8.240/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=143, cidr = cidr("192.168.8.248/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=144, cidr = cidr("192.168.9.0/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=144, cidr = cidr("192.168.9.8/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=145, cidr = cidr("192.168.9.16/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=145, cidr = cidr("192.168.9.24/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=146, cidr = cidr("192.168.9.32/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=146, cidr = cidr("192.168.9.40/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=147, cidr = cidr("192.168.9.48/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=147, cidr = cidr("192.168.9.56/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=148, cidr = cidr("192.168.9.64/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=148, cidr = cidr("192.168.9.72/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=149, cidr = cidr("192.168.9.80/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=149, cidr = cidr("192.168.9.88/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=150, cidr = cidr("192.168.9.96/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=150, cidr = cidr("192.168.9.104/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=151, cidr = cidr("192.168.9.112/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=151, cidr = cidr("192.168.9.120/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=152, cidr = cidr("192.168.9.128/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=152, cidr = cidr("192.168.9.136/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=153, cidr = cidr("192.168.9.144/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=153, cidr = cidr("192.168.9.152/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=154, cidr = cidr("192.168.9.160/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=154, cidr = cidr("192.168.9.168/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=155, cidr = cidr("192.168.9.176/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=155, cidr = cidr("192.168.9.184/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=156, cidr = cidr("192.168.9.192/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=156, cidr = cidr("192.168.9.200/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=157, cidr = cidr("192.168.9.208/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=157, cidr = cidr("192.168.9.216/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=158, cidr = cidr("192.168.9.224/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=158, cidr = cidr("192.168.9.232/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=159, cidr = cidr("192.168.9.240/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=159, cidr = cidr("192.168.9.248/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=160, cidr = cidr("192.168.10.0/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=160, cidr = cidr("192.168.10.8/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=161, cidr = cidr("192.168.10.16/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=161, cidr = cidr("192.168.10.24/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=162, cidr = cidr("192.168.10.32/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=162, cidr = cidr("192.168.10.40/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=163, cidr = cidr("192.168.10.48/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=163, cidr = cidr("192.168.10.56/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=164, cidr = cidr("192.168.10.64/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=164, cidr = cidr("192.168.10.72/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=165, cidr = cidr("192.168.10.80/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=165, cidr = cidr("192.168.10.88/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=166, cidr = cidr("192.168.10.96/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=166, cidr = cidr("192.168.10.104/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=167, cidr = cidr("192.168.10.112/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=167, cidr = cidr("192.168.10.120/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=168, cidr = cidr("192.168.10.128/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=168, cidr = cidr("192.168.10.136/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=169, cidr = cidr("192.168.10.144/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=169, cidr = cidr("192.168.10.152/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=170, cidr = cidr("192.168.10.160/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=170, cidr = cidr("192.168.10.168/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=171, cidr = cidr("192.168.10.176/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=171, cidr = cidr("192.168.10.184/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=172, cidr = cidr("192.168.10.192/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=172, cidr = cidr("192.168.10.200/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=173, cidr = cidr("192.168.10.208/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=173, cidr = cidr("192.168.10.216/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=174, cidr = cidr("192.168.10.224/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=174, cidr = cidr("192.168.10.232/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=175, cidr = cidr("192.168.10.240/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=175, cidr = cidr("192.168.10.248/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=176, cidr = cidr("192.168.11.0/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=176, cidr = cidr("192.168.11.8/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=177, cidr = cidr("192.168.11.16/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=177, cidr = cidr("192.168.11.24/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=178, cidr = cidr("192.168.11.32/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=178, cidr = cidr("192.168.11.40/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=179, cidr = cidr("192.168.11.48/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=179, cidr = cidr("192.168.11.56/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=180, cidr = cidr("192.168.11.64/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=180, cidr = cidr("192.168.11.72/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=181, cidr = cidr("192.168.11.80/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=181, cidr = cidr("192.168.11.88/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=182, cidr = cidr("192.168.11.96/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=182, cidr = cidr("192.168.11.104/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=183, cidr = cidr("192.168.11.112/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=183, cidr = cidr("192.168.11.120/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=184, cidr = cidr("192.168.11.128/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=184, cidr = cidr("192.168.11.136/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=185, cidr = cidr("192.168.11.144/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=185, cidr = cidr("192.168.11.152/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=186, cidr = cidr("192.168.11.160/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=186, cidr = cidr("192.168.11.168/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=187, cidr = cidr("192.168.11.176/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=187, cidr = cidr("192.168.11.184/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=188, cidr = cidr("192.168.11.192/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=188, cidr = cidr("192.168.11.200/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=189, cidr = cidr("192.168.11.208/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=189, cidr = cidr("192.168.11.216/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=190, cidr = cidr("192.168.11.224/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=190, cidr = cidr("192.168.11.232/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=191, cidr = cidr("192.168.11.240/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=191, cidr = cidr("192.168.11.248/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=192, cidr = cidr("192.168.12.0/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=192, cidr = cidr("192.168.12.8/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=193, cidr = cidr("192.168.12.16/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=193, cidr = cidr("192.168.12.24/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=194, cidr = cidr("192.168.12.32/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=194, cidr = cidr("192.168.12.40/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=195, cidr = cidr("192.168.12.48/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=195, cidr = cidr("192.168.12.56/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=196, cidr = cidr("192.168.12.64/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=196, cidr = cidr("192.168.12.72/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=197, cidr = cidr("192.168.12.80/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=197, cidr = cidr("192.168.12.88/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=198, cidr = cidr("192.168.12.96/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=198, cidr = cidr("192.168.12.104/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=199, cidr = cidr("192.168.12.112/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=199, cidr = cidr("192.168.12.120/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=200, cidr = cidr("192.168.12.128/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=200, cidr = cidr("192.168.12.136/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=201, cidr = cidr("192.168.12.144/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=201, cidr = cidr("192.168.12.152/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=202, cidr = cidr("192.168.12.160/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=202, cidr = cidr("192.168.12.168/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=203, cidr = cidr("192.168.12.176/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=203, cidr = cidr("192.168.12.184/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=204, cidr = cidr("192.168.12.192/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=204, cidr = cidr("192.168.12.200/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=205, cidr = cidr("192.168.12.208/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=205, cidr = cidr("192.168.12.216/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=206, cidr = cidr("192.168.12.224/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=206, cidr = cidr("192.168.12.232/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=207, cidr = cidr("192.168.12.240/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=207, cidr = cidr("192.168.12.248/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=208, cidr = cidr("192.168.13.0/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=208, cidr = cidr("192.168.13.8/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=209, cidr = cidr("192.168.13.16/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=209, cidr = cidr("192.168.13.24/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=210, cidr = cidr("192.168.13.32/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=210, cidr = cidr("192.168.13.40/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=211, cidr = cidr("192.168.13.48/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=211, cidr = cidr("192.168.13.56/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=212, cidr = cidr("192.168.13.64/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=212, cidr = cidr("192.168.13.72/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=213, cidr = cidr("192.168.13.80/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=213, cidr = cidr("192.168.13.88/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=214, cidr = cidr("192.168.13.96/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=214, cidr = cidr("192.168.13.104/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=215, cidr = cidr("192.168.13.112/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=215, cidr = cidr("192.168.13.120/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=216, cidr = cidr("192.168.13.128/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=216, cidr = cidr("192.168.13.136/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=217, cidr = cidr("192.168.13.144/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=217, cidr = cidr("192.168.13.152/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=218, cidr = cidr("192.168.13.160/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=218, cidr = cidr("192.168.13.168/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=219, cidr = cidr("192.168.13.176/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=219, cidr = cidr("192.168.13.184/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=220, cidr = cidr("192.168.13.192/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=220, cidr = cidr("192.168.13.200/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=221, cidr = cidr("192.168.13.208/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=221, cidr = cidr("192.168.13.216/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=222, cidr = cidr("192.168.13.224/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=222, cidr = cidr("192.168.13.232/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=223, cidr = cidr("192.168.13.240/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=223, cidr = cidr("192.168.13.248/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=224, cidr = cidr("192.168.14.0/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=224, cidr = cidr("192.168.14.8/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=225, cidr = cidr("192.168.14.16/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=225, cidr = cidr("192.168.14.24/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=226, cidr = cidr("192.168.14.32/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=226, cidr = cidr("192.168.14.40/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=227, cidr = cidr("192.168.14.48/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=227, cidr = cidr("192.168.14.56/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=228, cidr = cidr("192.168.14.64/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=228, cidr = cidr("192.168.14.72/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=229, cidr = cidr("192.168.14.80/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=229, cidr = cidr("192.168.14.88/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=230, cidr = cidr("192.168.14.96/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=230, cidr = cidr("192.168.14.104/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=231, cidr = cidr("192.168.14.112/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=231, cidr = cidr("192.168.14.120/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=232, cidr = cidr("192.168.14.128/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=232, cidr = cidr("192.168.14.136/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=233, cidr = cidr("192.168.14.144/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=233, cidr = cidr("192.168.14.152/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=234, cidr = cidr("192.168.14.160/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=234, cidr = cidr("192.168.14.168/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=235, cidr = cidr("192.168.14.176/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=235, cidr = cidr("192.168.14.184/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=236, cidr = cidr("192.168.14.192/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=236, cidr = cidr("192.168.14.200/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=237, cidr = cidr("192.168.14.208/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=237, cidr = cidr("192.168.14.216/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=238, cidr = cidr("192.168.14.224/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=238, cidr = cidr("192.168.14.232/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=239, cidr = cidr("192.168.14.240/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=239, cidr = cidr("192.168.14.248/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=240, cidr = cidr("192.168.15.0/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=240, cidr = cidr("192.168.15.8/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=241, cidr = cidr("192.168.15.16/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=241, cidr = cidr("192.168.15.24/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=242, cidr = cidr("192.168.15.32/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=242, cidr = cidr("192.168.15.40/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=243, cidr = cidr("192.168.15.48/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=243, cidr = cidr("192.168.15.56/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=244, cidr = cidr("192.168.15.64/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=244, cidr = cidr("192.168.15.72/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=245, cidr = cidr("192.168.15.80/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=245, cidr = cidr("192.168.15.88/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=246, cidr = cidr("192.168.15.96/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=246, cidr = cidr("192.168.15.104/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=247, cidr = cidr("192.168.15.112/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=247, cidr = cidr("192.168.15.120/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=248, cidr = cidr("192.168.15.128/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=248, cidr = cidr("192.168.15.136/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=249, cidr = cidr("192.168.15.144/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=249, cidr = cidr("192.168.15.152/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=250, cidr = cidr("192.168.15.160/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=250, cidr = cidr("192.168.15.168/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=251, cidr = cidr("192.168.15.176/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=251, cidr = cidr("192.168.15.184/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=252, cidr = cidr("192.168.15.192/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=252, cidr = cidr("192.168.15.200/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=253, cidr = cidr("192.168.15.208/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=253, cidr = cidr("192.168.15.216/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=254, cidr = cidr("192.168.15.224/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=254, cidr = cidr("192.168.15.232/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=0, gre_id=0, svlan_id=0, cvlan_id=255, cidr = cidr("192.168.15.240/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=0, gre_id=0, svlan_id=1, cvlan_id=255, cidr = cidr("192.168.15.248/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=0, cidr = cidr("192.168.16.0/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=0, cidr = cidr("192.168.16.8/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=1, cidr = cidr("192.168.16.16/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=1, cidr = cidr("192.168.16.24/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=2, cidr = cidr("192.168.16.32/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=2, cidr = cidr("192.168.16.40/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=3, cidr = cidr("192.168.16.48/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=3, cidr = cidr("192.168.16.56/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=4, cidr = cidr("192.168.16.64/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=4, cidr = cidr("192.168.16.72/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=5, cidr = cidr("192.168.16.80/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=5, cidr = cidr("192.168.16.88/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=6, cidr = cidr("192.168.16.96/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=6, cidr = cidr("192.168.16.104/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=7, cidr = cidr("192.168.16.112/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=7, cidr = cidr("192.168.16.120/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=8, cidr = cidr("192.168.16.128/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=8, cidr = cidr("192.168.16.136/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=9, cidr = cidr("192.168.16.144/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=9, cidr = cidr("192.168.16.152/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=10, cidr = cidr("192.168.16.160/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=10, cidr = cidr("192.168.16.168/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=11, cidr = cidr("192.168.16.176/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=11, cidr = cidr("192.168.16.184/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=12, cidr = cidr("192.168.16.192/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=12, cidr = cidr("192.168.16.200/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=13, cidr = cidr("192.168.16.208/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=13, cidr = cidr("192.168.16.216/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=14, cidr = cidr("192.168.16.224/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=14, cidr = cidr("192.168.16.232/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=15, cidr = cidr("192.168.16.240/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=15, cidr = cidr("192.168.16.248/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=16, cidr = cidr("192.168.17.0/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=16, cidr = cidr("192.168.17.8/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=17, cidr = cidr("192.168.17.16/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=17, cidr = cidr("192.168.17.24/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=18, cidr = cidr("192.168.17.32/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=18, cidr = cidr("192.168.17.40/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=19, cidr = cidr("192.168.17.48/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=19, cidr = cidr("192.168.17.56/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=20, cidr = cidr("192.168.17.64/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=20, cidr = cidr("192.168.17.72/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=21, cidr = cidr("192.168.17.80/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=21, cidr = cidr("192.168.17.88/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=22, cidr = cidr("192.168.17.96/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=22, cidr = cidr("192.168.17.104/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=23, cidr = cidr("192.168.17.112/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=23, cidr = cidr("192.168.17.120/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=24, cidr = cidr("192.168.17.128/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=24, cidr = cidr("192.168.17.136/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=25, cidr = cidr("192.168.17.144/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=25, cidr = cidr("192.168.17.152/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=26, cidr = cidr("192.168.17.160/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=26, cidr = cidr("192.168.17.168/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=27, cidr = cidr("192.168.17.176/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=27, cidr = cidr("192.168.17.184/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=28, cidr = cidr("192.168.17.192/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=28, cidr = cidr("192.168.17.200/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=29, cidr = cidr("192.168.17.208/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=29, cidr = cidr("192.168.17.216/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=30, cidr = cidr("192.168.17.224/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=30, cidr = cidr("192.168.17.232/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=31, cidr = cidr("192.168.17.240/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=31, cidr = cidr("192.168.17.248/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=32, cidr = cidr("192.168.18.0/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=32, cidr = cidr("192.168.18.8/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=33, cidr = cidr("192.168.18.16/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=33, cidr = cidr("192.168.18.24/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=34, cidr = cidr("192.168.18.32/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=34, cidr = cidr("192.168.18.40/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=35, cidr = cidr("192.168.18.48/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=35, cidr = cidr("192.168.18.56/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=36, cidr = cidr("192.168.18.64/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=36, cidr = cidr("192.168.18.72/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=37, cidr = cidr("192.168.18.80/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=37, cidr = cidr("192.168.18.88/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=38, cidr = cidr("192.168.18.96/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=38, cidr = cidr("192.168.18.104/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=39, cidr = cidr("192.168.18.112/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=39, cidr = cidr("192.168.18.120/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=40, cidr = cidr("192.168.18.128/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=40, cidr = cidr("192.168.18.136/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=41, cidr = cidr("192.168.18.144/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=41, cidr = cidr("192.168.18.152/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=42, cidr = cidr("192.168.18.160/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=42, cidr = cidr("192.168.18.168/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=43, cidr = cidr("192.168.18.176/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=43, cidr = cidr("192.168.18.184/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=44, cidr = cidr("192.168.18.192/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=44, cidr = cidr("192.168.18.200/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=45, cidr = cidr("192.168.18.208/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=45, cidr = cidr("192.168.18.216/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=46, cidr = cidr("192.168.18.224/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=46, cidr = cidr("192.168.18.232/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=47, cidr = cidr("192.168.18.240/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=47, cidr = cidr("192.168.18.248/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=48, cidr = cidr("192.168.19.0/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=48, cidr = cidr("192.168.19.8/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=49, cidr = cidr("192.168.19.16/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=49, cidr = cidr("192.168.19.24/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=50, cidr = cidr("192.168.19.32/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=50, cidr = cidr("192.168.19.40/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=51, cidr = cidr("192.168.19.48/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=51, cidr = cidr("192.168.19.56/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=52, cidr = cidr("192.168.19.64/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=52, cidr = cidr("192.168.19.72/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=53, cidr = cidr("192.168.19.80/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=53, cidr = cidr("192.168.19.88/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=54, cidr = cidr("192.168.19.96/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=54, cidr = cidr("192.168.19.104/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=55, cidr = cidr("192.168.19.112/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=55, cidr = cidr("192.168.19.120/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=56, cidr = cidr("192.168.19.128/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=56, cidr = cidr("192.168.19.136/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=57, cidr = cidr("192.168.19.144/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=57, cidr = cidr("192.168.19.152/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=58, cidr = cidr("192.168.19.160/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=58, cidr = cidr("192.168.19.168/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=59, cidr = cidr("192.168.19.176/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=59, cidr = cidr("192.168.19.184/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=60, cidr = cidr("192.168.19.192/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=60, cidr = cidr("192.168.19.200/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=61, cidr = cidr("192.168.19.208/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=61, cidr = cidr("192.168.19.216/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=62, cidr = cidr("192.168.19.224/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=62, cidr = cidr("192.168.19.232/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=63, cidr = cidr("192.168.19.240/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=63, cidr = cidr("192.168.19.248/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=64, cidr = cidr("192.168.20.0/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=64, cidr = cidr("192.168.20.8/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=65, cidr = cidr("192.168.20.16/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=65, cidr = cidr("192.168.20.24/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=66, cidr = cidr("192.168.20.32/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=66, cidr = cidr("192.168.20.40/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=67, cidr = cidr("192.168.20.48/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=67, cidr = cidr("192.168.20.56/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=68, cidr = cidr("192.168.20.64/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=68, cidr = cidr("192.168.20.72/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=69, cidr = cidr("192.168.20.80/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=69, cidr = cidr("192.168.20.88/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=70, cidr = cidr("192.168.20.96/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=70, cidr = cidr("192.168.20.104/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=71, cidr = cidr("192.168.20.112/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=71, cidr = cidr("192.168.20.120/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=72, cidr = cidr("192.168.20.128/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=72, cidr = cidr("192.168.20.136/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=73, cidr = cidr("192.168.20.144/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=73, cidr = cidr("192.168.20.152/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=74, cidr = cidr("192.168.20.160/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=74, cidr = cidr("192.168.20.168/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=75, cidr = cidr("192.168.20.176/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=75, cidr = cidr("192.168.20.184/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=76, cidr = cidr("192.168.20.192/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=76, cidr = cidr("192.168.20.200/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=77, cidr = cidr("192.168.20.208/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=77, cidr = cidr("192.168.20.216/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=78, cidr = cidr("192.168.20.224/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=78, cidr = cidr("192.168.20.232/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=79, cidr = cidr("192.168.20.240/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=79, cidr = cidr("192.168.20.248/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=80, cidr = cidr("192.168.21.0/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=80, cidr = cidr("192.168.21.8/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=81, cidr = cidr("192.168.21.16/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=81, cidr = cidr("192.168.21.24/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=82, cidr = cidr("192.168.21.32/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=82, cidr = cidr("192.168.21.40/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=83, cidr = cidr("192.168.21.48/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=83, cidr = cidr("192.168.21.56/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=84, cidr = cidr("192.168.21.64/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=84, cidr = cidr("192.168.21.72/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=85, cidr = cidr("192.168.21.80/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=85, cidr = cidr("192.168.21.88/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=86, cidr = cidr("192.168.21.96/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=86, cidr = cidr("192.168.21.104/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=87, cidr = cidr("192.168.21.112/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=87, cidr = cidr("192.168.21.120/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=88, cidr = cidr("192.168.21.128/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=88, cidr = cidr("192.168.21.136/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=89, cidr = cidr("192.168.21.144/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=89, cidr = cidr("192.168.21.152/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=90, cidr = cidr("192.168.21.160/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=90, cidr = cidr("192.168.21.168/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=91, cidr = cidr("192.168.21.176/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=91, cidr = cidr("192.168.21.184/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=92, cidr = cidr("192.168.21.192/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=92, cidr = cidr("192.168.21.200/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=93, cidr = cidr("192.168.21.208/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=93, cidr = cidr("192.168.21.216/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=94, cidr = cidr("192.168.21.224/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=94, cidr = cidr("192.168.21.232/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=95, cidr = cidr("192.168.21.240/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=95, cidr = cidr("192.168.21.248/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=96, cidr = cidr("192.168.22.0/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=96, cidr = cidr("192.168.22.8/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=97, cidr = cidr("192.168.22.16/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=97, cidr = cidr("192.168.22.24/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=98, cidr = cidr("192.168.22.32/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=98, cidr = cidr("192.168.22.40/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=99, cidr = cidr("192.168.22.48/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=99, cidr = cidr("192.168.22.56/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=100, cidr = cidr("192.168.22.64/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=100, cidr = cidr("192.168.22.72/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=101, cidr = cidr("192.168.22.80/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=101, cidr = cidr("192.168.22.88/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=102, cidr = cidr("192.168.22.96/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=102, cidr = cidr("192.168.22.104/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=103, cidr = cidr("192.168.22.112/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=103, cidr = cidr("192.168.22.120/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=104, cidr = cidr("192.168.22.128/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=104, cidr = cidr("192.168.22.136/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=105, cidr = cidr("192.168.22.144/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=105, cidr = cidr("192.168.22.152/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=106, cidr = cidr("192.168.22.160/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=106, cidr = cidr("192.168.22.168/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=107, cidr = cidr("192.168.22.176/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=107, cidr = cidr("192.168.22.184/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=108, cidr = cidr("192.168.22.192/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=108, cidr = cidr("192.168.22.200/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=109, cidr = cidr("192.168.22.208/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=109, cidr = cidr("192.168.22.216/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=110, cidr = cidr("192.168.22.224/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=110, cidr = cidr("192.168.22.232/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=111, cidr = cidr("192.168.22.240/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=111, cidr = cidr("192.168.22.248/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=112, cidr = cidr("192.168.23.0/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=112, cidr = cidr("192.168.23.8/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=113, cidr = cidr("192.168.23.16/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=113, cidr = cidr("192.168.23.24/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=114, cidr = cidr("192.168.23.32/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=114, cidr = cidr("192.168.23.40/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=115, cidr = cidr("192.168.23.48/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=115, cidr = cidr("192.168.23.56/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=116, cidr = cidr("192.168.23.64/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=116, cidr = cidr("192.168.23.72/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=117, cidr = cidr("192.168.23.80/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=117, cidr = cidr("192.168.23.88/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=118, cidr = cidr("192.168.23.96/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=118, cidr = cidr("192.168.23.104/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=119, cidr = cidr("192.168.23.112/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=119, cidr = cidr("192.168.23.120/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=120, cidr = cidr("192.168.23.128/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=120, cidr = cidr("192.168.23.136/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=121, cidr = cidr("192.168.23.144/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=121, cidr = cidr("192.168.23.152/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=122, cidr = cidr("192.168.23.160/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=122, cidr = cidr("192.168.23.168/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=123, cidr = cidr("192.168.23.176/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=123, cidr = cidr("192.168.23.184/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=124, cidr = cidr("192.168.23.192/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=124, cidr = cidr("192.168.23.200/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=125, cidr = cidr("192.168.23.208/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=125, cidr = cidr("192.168.23.216/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=126, cidr = cidr("192.168.23.224/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=126, cidr = cidr("192.168.23.232/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=127, cidr = cidr("192.168.23.240/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=127, cidr = cidr("192.168.23.248/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=128, cidr = cidr("192.168.24.0/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=128, cidr = cidr("192.168.24.8/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=129, cidr = cidr("192.168.24.16/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=129, cidr = cidr("192.168.24.24/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=130, cidr = cidr("192.168.24.32/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=130, cidr = cidr("192.168.24.40/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=131, cidr = cidr("192.168.24.48/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=131, cidr = cidr("192.168.24.56/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=132, cidr = cidr("192.168.24.64/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=132, cidr = cidr("192.168.24.72/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=133, cidr = cidr("192.168.24.80/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=133, cidr = cidr("192.168.24.88/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=134, cidr = cidr("192.168.24.96/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=134, cidr = cidr("192.168.24.104/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=135, cidr = cidr("192.168.24.112/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=135, cidr = cidr("192.168.24.120/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=136, cidr = cidr("192.168.24.128/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=136, cidr = cidr("192.168.24.136/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=137, cidr = cidr("192.168.24.144/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=137, cidr = cidr("192.168.24.152/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=138, cidr = cidr("192.168.24.160/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=138, cidr = cidr("192.168.24.168/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=139, cidr = cidr("192.168.24.176/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=139, cidr = cidr("192.168.24.184/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=140, cidr = cidr("192.168.24.192/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=140, cidr = cidr("192.168.24.200/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=141, cidr = cidr("192.168.24.208/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=141, cidr = cidr("192.168.24.216/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=142, cidr = cidr("192.168.24.224/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=142, cidr = cidr("192.168.24.232/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=143, cidr = cidr("192.168.24.240/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=143, cidr = cidr("192.168.24.248/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=144, cidr = cidr("192.168.25.0/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=144, cidr = cidr("192.168.25.8/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=145, cidr = cidr("192.168.25.16/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=145, cidr = cidr("192.168.25.24/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=146, cidr = cidr("192.168.25.32/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=146, cidr = cidr("192.168.25.40/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=147, cidr = cidr("192.168.25.48/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=147, cidr = cidr("192.168.25.56/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=148, cidr = cidr("192.168.25.64/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=148, cidr = cidr("192.168.25.72/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=149, cidr = cidr("192.168.25.80/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=149, cidr = cidr("192.168.25.88/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=150, cidr = cidr("192.168.25.96/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=150, cidr = cidr("192.168.25.104/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=151, cidr = cidr("192.168.25.112/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=151, cidr = cidr("192.168.25.120/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=152, cidr = cidr("192.168.25.128/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=152, cidr = cidr("192.168.25.136/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=153, cidr = cidr("192.168.25.144/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=153, cidr = cidr("192.168.25.152/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=154, cidr = cidr("192.168.25.160/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=154, cidr = cidr("192.168.25.168/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=155, cidr = cidr("192.168.25.176/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=155, cidr = cidr("192.168.25.184/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=156, cidr = cidr("192.168.25.192/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=156, cidr = cidr("192.168.25.200/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=157, cidr = cidr("192.168.25.208/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=157, cidr = cidr("192.168.25.216/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=158, cidr = cidr("192.168.25.224/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=158, cidr = cidr("192.168.25.232/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=159, cidr = cidr("192.168.25.240/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=159, cidr = cidr("192.168.25.248/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=160, cidr = cidr("192.168.26.0/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=160, cidr = cidr("192.168.26.8/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=161, cidr = cidr("192.168.26.16/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=161, cidr = cidr("192.168.26.24/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=162, cidr = cidr("192.168.26.32/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=162, cidr = cidr("192.168.26.40/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=163, cidr = cidr("192.168.26.48/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=163, cidr = cidr("192.168.26.56/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=164, cidr = cidr("192.168.26.64/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=164, cidr = cidr("192.168.26.72/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=165, cidr = cidr("192.168.26.80/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=165, cidr = cidr("192.168.26.88/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=166, cidr = cidr("192.168.26.96/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=166, cidr = cidr("192.168.26.104/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=167, cidr = cidr("192.168.26.112/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=167, cidr = cidr("192.168.26.120/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=168, cidr = cidr("192.168.26.128/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=168, cidr = cidr("192.168.26.136/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=169, cidr = cidr("192.168.26.144/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=169, cidr = cidr("192.168.26.152/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=170, cidr = cidr("192.168.26.160/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=170, cidr = cidr("192.168.26.168/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=171, cidr = cidr("192.168.26.176/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=171, cidr = cidr("192.168.26.184/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=172, cidr = cidr("192.168.26.192/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=172, cidr = cidr("192.168.26.200/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=173, cidr = cidr("192.168.26.208/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=173, cidr = cidr("192.168.26.216/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=174, cidr = cidr("192.168.26.224/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=174, cidr = cidr("192.168.26.232/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=175, cidr = cidr("192.168.26.240/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=175, cidr = cidr("192.168.26.248/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=176, cidr = cidr("192.168.27.0/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=176, cidr = cidr("192.168.27.8/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=177, cidr = cidr("192.168.27.16/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=177, cidr = cidr("192.168.27.24/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=178, cidr = cidr("192.168.27.32/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=178, cidr = cidr("192.168.27.40/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=179, cidr = cidr("192.168.27.48/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=179, cidr = cidr("192.168.27.56/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=180, cidr = cidr("192.168.27.64/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=180, cidr = cidr("192.168.27.72/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=181, cidr = cidr("192.168.27.80/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=181, cidr = cidr("192.168.27.88/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=182, cidr = cidr("192.168.27.96/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=182, cidr = cidr("192.168.27.104/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=183, cidr = cidr("192.168.27.112/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=183, cidr = cidr("192.168.27.120/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=184, cidr = cidr("192.168.27.128/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=184, cidr = cidr("192.168.27.136/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=185, cidr = cidr("192.168.27.144/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=185, cidr = cidr("192.168.27.152/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=186, cidr = cidr("192.168.27.160/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=186, cidr = cidr("192.168.27.168/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=187, cidr = cidr("192.168.27.176/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=187, cidr = cidr("192.168.27.184/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=188, cidr = cidr("192.168.27.192/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=188, cidr = cidr("192.168.27.200/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=189, cidr = cidr("192.168.27.208/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=189, cidr = cidr("192.168.27.216/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=190, cidr = cidr("192.168.27.224/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=190, cidr = cidr("192.168.27.232/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=191, cidr = cidr("192.168.27.240/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=191, cidr = cidr("192.168.27.248/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=192, cidr = cidr("192.168.28.0/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=192, cidr = cidr("192.168.28.8/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=193, cidr = cidr("192.168.28.16/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=193, cidr = cidr("192.168.28.24/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=194, cidr = cidr("192.168.28.32/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=194, cidr = cidr("192.168.28.40/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=195, cidr = cidr("192.168.28.48/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=195, cidr = cidr("192.168.28.56/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=196, cidr = cidr("192.168.28.64/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=196, cidr = cidr("192.168.28.72/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=197, cidr = cidr("192.168.28.80/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=197, cidr = cidr("192.168.28.88/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=198, cidr = cidr("192.168.28.96/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=198, cidr = cidr("192.168.28.104/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=199, cidr = cidr("192.168.28.112/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=199, cidr = cidr("192.168.28.120/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=200, cidr = cidr("192.168.28.128/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=200, cidr = cidr("192.168.28.136/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=201, cidr = cidr("192.168.28.144/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=201, cidr = cidr("192.168.28.152/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=202, cidr = cidr("192.168.28.160/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=202, cidr = cidr("192.168.28.168/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=203, cidr = cidr("192.168.28.176/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=203, cidr = cidr("192.168.28.184/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=204, cidr = cidr("192.168.28.192/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=204, cidr = cidr("192.168.28.200/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=205, cidr = cidr("192.168.28.208/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=205, cidr = cidr("192.168.28.216/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=206, cidr = cidr("192.168.28.224/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=206, cidr = cidr("192.168.28.232/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=207, cidr = cidr("192.168.28.240/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=207, cidr = cidr("192.168.28.248/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=208, cidr = cidr("192.168.29.0/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=208, cidr = cidr("192.168.29.8/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=209, cidr = cidr("192.168.29.16/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=209, cidr = cidr("192.168.29.24/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=210, cidr = cidr("192.168.29.32/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=210, cidr = cidr("192.168.29.40/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=211, cidr = cidr("192.168.29.48/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=211, cidr = cidr("192.168.29.56/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=212, cidr = cidr("192.168.29.64/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=212, cidr = cidr("192.168.29.72/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=213, cidr = cidr("192.168.29.80/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=213, cidr = cidr("192.168.29.88/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=214, cidr = cidr("192.168.29.96/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=214, cidr = cidr("192.168.29.104/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=215, cidr = cidr("192.168.29.112/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=215, cidr = cidr("192.168.29.120/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=216, cidr = cidr("192.168.29.128/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=216, cidr = cidr("192.168.29.136/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=217, cidr = cidr("192.168.29.144/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=217, cidr = cidr("192.168.29.152/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=218, cidr = cidr("192.168.29.160/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=218, cidr = cidr("192.168.29.168/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=219, cidr = cidr("192.168.29.176/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=219, cidr = cidr("192.168.29.184/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=220, cidr = cidr("192.168.29.192/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=220, cidr = cidr("192.168.29.200/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=221, cidr = cidr("192.168.29.208/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=221, cidr = cidr("192.168.29.216/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=222, cidr = cidr("192.168.29.224/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=222, cidr = cidr("192.168.29.232/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=223, cidr = cidr("192.168.29.240/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=223, cidr = cidr("192.168.29.248/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=224, cidr = cidr("192.168.30.0/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=224, cidr = cidr("192.168.30.8/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=225, cidr = cidr("192.168.30.16/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=225, cidr = cidr("192.168.30.24/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=226, cidr = cidr("192.168.30.32/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=226, cidr = cidr("192.168.30.40/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=227, cidr = cidr("192.168.30.48/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=227, cidr = cidr("192.168.30.56/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=228, cidr = cidr("192.168.30.64/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=228, cidr = cidr("192.168.30.72/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=229, cidr = cidr("192.168.30.80/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=229, cidr = cidr("192.168.30.88/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=230, cidr = cidr("192.168.30.96/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=230, cidr = cidr("192.168.30.104/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=231, cidr = cidr("192.168.30.112/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=231, cidr = cidr("192.168.30.120/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=232, cidr = cidr("192.168.30.128/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=232, cidr = cidr("192.168.30.136/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=233, cidr = cidr("192.168.30.144/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=233, cidr = cidr("192.168.30.152/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=234, cidr = cidr("192.168.30.160/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=234, cidr = cidr("192.168.30.168/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=235, cidr = cidr("192.168.30.176/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=235, cidr = cidr("192.168.30.184/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=236, cidr = cidr("192.168.30.192/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=236, cidr = cidr("192.168.30.200/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=237, cidr = cidr("192.168.30.208/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=237, cidr = cidr("192.168.30.216/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=238, cidr = cidr("192.168.30.224/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=238, cidr = cidr("192.168.30.232/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=239, cidr = cidr("192.168.30.240/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=239, cidr = cidr("192.168.30.248/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=240, cidr = cidr("192.168.31.0/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=240, cidr = cidr("192.168.31.8/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=241, cidr = cidr("192.168.31.16/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=241, cidr = cidr("192.168.31.24/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=242, cidr = cidr("192.168.31.32/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=242, cidr = cidr("192.168.31.40/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=243, cidr = cidr("192.168.31.48/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=243, cidr = cidr("192.168.31.56/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=244, cidr = cidr("192.168.31.64/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=244, cidr = cidr("192.168.31.72/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=245, cidr = cidr("192.168.31.80/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=245, cidr = cidr("192.168.31.88/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=246, cidr = cidr("192.168.31.96/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=246, cidr = cidr("192.168.31.104/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=247, cidr = cidr("192.168.31.112/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=247, cidr = cidr("192.168.31.120/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=248, cidr = cidr("192.168.31.128/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=248, cidr = cidr("192.168.31.136/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=249, cidr = cidr("192.168.31.144/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=249, cidr = cidr("192.168.31.152/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=250, cidr = cidr("192.168.31.160/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=250, cidr = cidr("192.168.31.168/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=251, cidr = cidr("192.168.31.176/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=251, cidr = cidr("192.168.31.184/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=252, cidr = cidr("192.168.31.192/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=252, cidr = cidr("192.168.31.200/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=253, cidr = cidr("192.168.31.208/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=253, cidr = cidr("192.168.31.216/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=254, cidr = cidr("192.168.31.224/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=254, cidr = cidr("192.168.31.232/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=1, gre_id=0, svlan_id=16, cvlan_id=255, cidr = cidr("192.168.31.240/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=1, gre_id=0, svlan_id=17, cvlan_id=255, cidr = cidr("192.168.31.248/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=0, cidr = cidr("192.168.32.0/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=0, cidr = cidr("192.168.32.8/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=1, cidr = cidr("192.168.32.16/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=1, cidr = cidr("192.168.32.24/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=2, cidr = cidr("192.168.32.32/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=2, cidr = cidr("192.168.32.40/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=3, cidr = cidr("192.168.32.48/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=3, cidr = cidr("192.168.32.56/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=4, cidr = cidr("192.168.32.64/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=4, cidr = cidr("192.168.32.72/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=5, cidr = cidr("192.168.32.80/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=5, cidr = cidr("192.168.32.88/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=6, cidr = cidr("192.168.32.96/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=6, cidr = cidr("192.168.32.104/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=7, cidr = cidr("192.168.32.112/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=7, cidr = cidr("192.168.32.120/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=8, cidr = cidr("192.168.32.128/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=8, cidr = cidr("192.168.32.136/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=9, cidr = cidr("192.168.32.144/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=9, cidr = cidr("192.168.32.152/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=10, cidr = cidr("192.168.32.160/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=10, cidr = cidr("192.168.32.168/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=11, cidr = cidr("192.168.32.176/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=11, cidr = cidr("192.168.32.184/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=12, cidr = cidr("192.168.32.192/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=12, cidr = cidr("192.168.32.200/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=13, cidr = cidr("192.168.32.208/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=13, cidr = cidr("192.168.32.216/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=14, cidr = cidr("192.168.32.224/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=14, cidr = cidr("192.168.32.232/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=15, cidr = cidr("192.168.32.240/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=15, cidr = cidr("192.168.32.248/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=16, cidr = cidr("192.168.33.0/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=16, cidr = cidr("192.168.33.8/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=17, cidr = cidr("192.168.33.16/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=17, cidr = cidr("192.168.33.24/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=18, cidr = cidr("192.168.33.32/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=18, cidr = cidr("192.168.33.40/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=19, cidr = cidr("192.168.33.48/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=19, cidr = cidr("192.168.33.56/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=20, cidr = cidr("192.168.33.64/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=20, cidr = cidr("192.168.33.72/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=21, cidr = cidr("192.168.33.80/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=21, cidr = cidr("192.168.33.88/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=22, cidr = cidr("192.168.33.96/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=22, cidr = cidr("192.168.33.104/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=23, cidr = cidr("192.168.33.112/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=23, cidr = cidr("192.168.33.120/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=24, cidr = cidr("192.168.33.128/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=24, cidr = cidr("192.168.33.136/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=25, cidr = cidr("192.168.33.144/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=25, cidr = cidr("192.168.33.152/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=26, cidr = cidr("192.168.33.160/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=26, cidr = cidr("192.168.33.168/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=27, cidr = cidr("192.168.33.176/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=27, cidr = cidr("192.168.33.184/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=28, cidr = cidr("192.168.33.192/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=28, cidr = cidr("192.168.33.200/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=29, cidr = cidr("192.168.33.208/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=29, cidr = cidr("192.168.33.216/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=30, cidr = cidr("192.168.33.224/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=30, cidr = cidr("192.168.33.232/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=31, cidr = cidr("192.168.33.240/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=31, cidr = cidr("192.168.33.248/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=32, cidr = cidr("192.168.34.0/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=32, cidr = cidr("192.168.34.8/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=33, cidr = cidr("192.168.34.16/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=33, cidr = cidr("192.168.34.24/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=34, cidr = cidr("192.168.34.32/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=34, cidr = cidr("192.168.34.40/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=35, cidr = cidr("192.168.34.48/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=35, cidr = cidr("192.168.34.56/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=36, cidr = cidr("192.168.34.64/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=36, cidr = cidr("192.168.34.72/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=37, cidr = cidr("192.168.34.80/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=37, cidr = cidr("192.168.34.88/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=38, cidr = cidr("192.168.34.96/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=38, cidr = cidr("192.168.34.104/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=39, cidr = cidr("192.168.34.112/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=39, cidr = cidr("192.168.34.120/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=40, cidr = cidr("192.168.34.128/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=40, cidr = cidr("192.168.34.136/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=41, cidr = cidr("192.168.34.144/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=41, cidr = cidr("192.168.34.152/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=42, cidr = cidr("192.168.34.160/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=42, cidr = cidr("192.168.34.168/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=43, cidr = cidr("192.168.34.176/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=43, cidr = cidr("192.168.34.184/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=44, cidr = cidr("192.168.34.192/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=44, cidr = cidr("192.168.34.200/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=45, cidr = cidr("192.168.34.208/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=45, cidr = cidr("192.168.34.216/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=46, cidr = cidr("192.168.34.224/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=46, cidr = cidr("192.168.34.232/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=47, cidr = cidr("192.168.34.240/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=47, cidr = cidr("192.168.34.248/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=48, cidr = cidr("192.168.35.0/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=48, cidr = cidr("192.168.35.8/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=49, cidr = cidr("192.168.35.16/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=49, cidr = cidr("192.168.35.24/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=50, cidr = cidr("192.168.35.32/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=50, cidr = cidr("192.168.35.40/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=51, cidr = cidr("192.168.35.48/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=51, cidr = cidr("192.168.35.56/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=52, cidr = cidr("192.168.35.64/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=52, cidr = cidr("192.168.35.72/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=53, cidr = cidr("192.168.35.80/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=53, cidr = cidr("192.168.35.88/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=54, cidr = cidr("192.168.35.96/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=54, cidr = cidr("192.168.35.104/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=55, cidr = cidr("192.168.35.112/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=55, cidr = cidr("192.168.35.120/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=56, cidr = cidr("192.168.35.128/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=56, cidr = cidr("192.168.35.136/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=57, cidr = cidr("192.168.35.144/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=57, cidr = cidr("192.168.35.152/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=58, cidr = cidr("192.168.35.160/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=58, cidr = cidr("192.168.35.168/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=59, cidr = cidr("192.168.35.176/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=59, cidr = cidr("192.168.35.184/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=60, cidr = cidr("192.168.35.192/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=60, cidr = cidr("192.168.35.200/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=61, cidr = cidr("192.168.35.208/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=61, cidr = cidr("192.168.35.216/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=62, cidr = cidr("192.168.35.224/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=62, cidr = cidr("192.168.35.232/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=63, cidr = cidr("192.168.35.240/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=63, cidr = cidr("192.168.35.248/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=64, cidr = cidr("192.168.36.0/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=64, cidr = cidr("192.168.36.8/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=65, cidr = cidr("192.168.36.16/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=65, cidr = cidr("192.168.36.24/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=66, cidr = cidr("192.168.36.32/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=66, cidr = cidr("192.168.36.40/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=67, cidr = cidr("192.168.36.48/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=67, cidr = cidr("192.168.36.56/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=68, cidr = cidr("192.168.36.64/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=68, cidr = cidr("192.168.36.72/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=69, cidr = cidr("192.168.36.80/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=69, cidr = cidr("192.168.36.88/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=70, cidr = cidr("192.168.36.96/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=70, cidr = cidr("192.168.36.104/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=71, cidr = cidr("192.168.36.112/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=71, cidr = cidr("192.168.36.120/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=72, cidr = cidr("192.168.36.128/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=72, cidr = cidr("192.168.36.136/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=73, cidr = cidr("192.168.36.144/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=73, cidr = cidr("192.168.36.152/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=74, cidr = cidr("192.168.36.160/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=74, cidr = cidr("192.168.36.168/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=75, cidr = cidr("192.168.36.176/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=75, cidr = cidr("192.168.36.184/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=76, cidr = cidr("192.168.36.192/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=76, cidr = cidr("192.168.36.200/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=77, cidr = cidr("192.168.36.208/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=77, cidr = cidr("192.168.36.216/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=78, cidr = cidr("192.168.36.224/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=78, cidr = cidr("192.168.36.232/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=79, cidr = cidr("192.168.36.240/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=79, cidr = cidr("192.168.36.248/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=80, cidr = cidr("192.168.37.0/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=80, cidr = cidr("192.168.37.8/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=81, cidr = cidr("192.168.37.16/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=81, cidr = cidr("192.168.37.24/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=82, cidr = cidr("192.168.37.32/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=82, cidr = cidr("192.168.37.40/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=83, cidr = cidr("192.168.37.48/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=83, cidr = cidr("192.168.37.56/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=84, cidr = cidr("192.168.37.64/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=84, cidr = cidr("192.168.37.72/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=85, cidr = cidr("192.168.37.80/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=85, cidr = cidr("192.168.37.88/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=86, cidr = cidr("192.168.37.96/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=86, cidr = cidr("192.168.37.104/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=87, cidr = cidr("192.168.37.112/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=87, cidr = cidr("192.168.37.120/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=88, cidr = cidr("192.168.37.128/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=88, cidr = cidr("192.168.37.136/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=89, cidr = cidr("192.168.37.144/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=89, cidr = cidr("192.168.37.152/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=90, cidr = cidr("192.168.37.160/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=90, cidr = cidr("192.168.37.168/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=91, cidr = cidr("192.168.37.176/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=91, cidr = cidr("192.168.37.184/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=92, cidr = cidr("192.168.37.192/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=92, cidr = cidr("192.168.37.200/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=93, cidr = cidr("192.168.37.208/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=93, cidr = cidr("192.168.37.216/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=94, cidr = cidr("192.168.37.224/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=94, cidr = cidr("192.168.37.232/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=95, cidr = cidr("192.168.37.240/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=95, cidr = cidr("192.168.37.248/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=96, cidr = cidr("192.168.38.0/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=96, cidr = cidr("192.168.38.8/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=97, cidr = cidr("192.168.38.16/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=97, cidr = cidr("192.168.38.24/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=98, cidr = cidr("192.168.38.32/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=98, cidr = cidr("192.168.38.40/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=99, cidr = cidr("192.168.38.48/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=99, cidr = cidr("192.168.38.56/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=100, cidr = cidr("192.168.38.64/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=100, cidr = cidr("192.168.38.72/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=101, cidr = cidr("192.168.38.80/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=101, cidr = cidr("192.168.38.88/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=102, cidr = cidr("192.168.38.96/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=102, cidr = cidr("192.168.38.104/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=103, cidr = cidr("192.168.38.112/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=103, cidr = cidr("192.168.38.120/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=104, cidr = cidr("192.168.38.128/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=104, cidr = cidr("192.168.38.136/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=105, cidr = cidr("192.168.38.144/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=105, cidr = cidr("192.168.38.152/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=106, cidr = cidr("192.168.38.160/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=106, cidr = cidr("192.168.38.168/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=107, cidr = cidr("192.168.38.176/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=107, cidr = cidr("192.168.38.184/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=108, cidr = cidr("192.168.38.192/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=108, cidr = cidr("192.168.38.200/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=109, cidr = cidr("192.168.38.208/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=109, cidr = cidr("192.168.38.216/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=110, cidr = cidr("192.168.38.224/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=110, cidr = cidr("192.168.38.232/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=111, cidr = cidr("192.168.38.240/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=111, cidr = cidr("192.168.38.248/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=112, cidr = cidr("192.168.39.0/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=112, cidr = cidr("192.168.39.8/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=113, cidr = cidr("192.168.39.16/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=113, cidr = cidr("192.168.39.24/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=114, cidr = cidr("192.168.39.32/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=114, cidr = cidr("192.168.39.40/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=115, cidr = cidr("192.168.39.48/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=115, cidr = cidr("192.168.39.56/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=116, cidr = cidr("192.168.39.64/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=116, cidr = cidr("192.168.39.72/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=117, cidr = cidr("192.168.39.80/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=117, cidr = cidr("192.168.39.88/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=118, cidr = cidr("192.168.39.96/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=118, cidr = cidr("192.168.39.104/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=119, cidr = cidr("192.168.39.112/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=119, cidr = cidr("192.168.39.120/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=120, cidr = cidr("192.168.39.128/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=120, cidr = cidr("192.168.39.136/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=121, cidr = cidr("192.168.39.144/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=121, cidr = cidr("192.168.39.152/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=122, cidr = cidr("192.168.39.160/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=122, cidr = cidr("192.168.39.168/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=123, cidr = cidr("192.168.39.176/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=123, cidr = cidr("192.168.39.184/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=124, cidr = cidr("192.168.39.192/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=124, cidr = cidr("192.168.39.200/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=125, cidr = cidr("192.168.39.208/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=125, cidr = cidr("192.168.39.216/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=126, cidr = cidr("192.168.39.224/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=126, cidr = cidr("192.168.39.232/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=127, cidr = cidr("192.168.39.240/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=127, cidr = cidr("192.168.39.248/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=128, cidr = cidr("192.168.40.0/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=128, cidr = cidr("192.168.40.8/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=129, cidr = cidr("192.168.40.16/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=129, cidr = cidr("192.168.40.24/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=130, cidr = cidr("192.168.40.32/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=130, cidr = cidr("192.168.40.40/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=131, cidr = cidr("192.168.40.48/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=131, cidr = cidr("192.168.40.56/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=132, cidr = cidr("192.168.40.64/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=132, cidr = cidr("192.168.40.72/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=133, cidr = cidr("192.168.40.80/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=133, cidr = cidr("192.168.40.88/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=134, cidr = cidr("192.168.40.96/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=134, cidr = cidr("192.168.40.104/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=135, cidr = cidr("192.168.40.112/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=135, cidr = cidr("192.168.40.120/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=136, cidr = cidr("192.168.40.128/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=136, cidr = cidr("192.168.40.136/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=137, cidr = cidr("192.168.40.144/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=137, cidr = cidr("192.168.40.152/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=138, cidr = cidr("192.168.40.160/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=138, cidr = cidr("192.168.40.168/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=139, cidr = cidr("192.168.40.176/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=139, cidr = cidr("192.168.40.184/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=140, cidr = cidr("192.168.40.192/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=140, cidr = cidr("192.168.40.200/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=141, cidr = cidr("192.168.40.208/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=141, cidr = cidr("192.168.40.216/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=142, cidr = cidr("192.168.40.224/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=142, cidr = cidr("192.168.40.232/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=143, cidr = cidr("192.168.40.240/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=143, cidr = cidr("192.168.40.248/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=144, cidr = cidr("192.168.41.0/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=144, cidr = cidr("192.168.41.8/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=145, cidr = cidr("192.168.41.16/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=145, cidr = cidr("192.168.41.24/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=146, cidr = cidr("192.168.41.32/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=146, cidr = cidr("192.168.41.40/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=147, cidr = cidr("192.168.41.48/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=147, cidr = cidr("192.168.41.56/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=148, cidr = cidr("192.168.41.64/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=148, cidr = cidr("192.168.41.72/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=149, cidr = cidr("192.168.41.80/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=149, cidr = cidr("192.168.41.88/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=150, cidr = cidr("192.168.41.96/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=150, cidr = cidr("192.168.41.104/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=151, cidr = cidr("192.168.41.112/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=151, cidr = cidr("192.168.41.120/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=152, cidr = cidr("192.168.41.128/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=152, cidr = cidr("192.168.41.136/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=153, cidr = cidr("192.168.41.144/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=153, cidr = cidr("192.168.41.152/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=154, cidr = cidr("192.168.41.160/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=154, cidr = cidr("192.168.41.168/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=155, cidr = cidr("192.168.41.176/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=155, cidr = cidr("192.168.41.184/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=156, cidr = cidr("192.168.41.192/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=156, cidr = cidr("192.168.41.200/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=157, cidr = cidr("192.168.41.208/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=157, cidr = cidr("192.168.41.216/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=158, cidr = cidr("192.168.41.224/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=158, cidr = cidr("192.168.41.232/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=159, cidr = cidr("192.168.41.240/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=159, cidr = cidr("192.168.41.248/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=160, cidr = cidr("192.168.42.0/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=160, cidr = cidr("192.168.42.8/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=161, cidr = cidr("192.168.42.16/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=161, cidr = cidr("192.168.42.24/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=162, cidr = cidr("192.168.42.32/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=162, cidr = cidr("192.168.42.40/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=163, cidr = cidr("192.168.42.48/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=163, cidr = cidr("192.168.42.56/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=164, cidr = cidr("192.168.42.64/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=164, cidr = cidr("192.168.42.72/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=165, cidr = cidr("192.168.42.80/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=165, cidr = cidr("192.168.42.88/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=166, cidr = cidr("192.168.42.96/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=166, cidr = cidr("192.168.42.104/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=167, cidr = cidr("192.168.42.112/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=167, cidr = cidr("192.168.42.120/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=168, cidr = cidr("192.168.42.128/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=168, cidr = cidr("192.168.42.136/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=169, cidr = cidr("192.168.42.144/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=169, cidr = cidr("192.168.42.152/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=170, cidr = cidr("192.168.42.160/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=170, cidr = cidr("192.168.42.168/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=171, cidr = cidr("192.168.42.176/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=171, cidr = cidr("192.168.42.184/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=172, cidr = cidr("192.168.42.192/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=172, cidr = cidr("192.168.42.200/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=173, cidr = cidr("192.168.42.208/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=173, cidr = cidr("192.168.42.216/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=174, cidr = cidr("192.168.42.224/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=174, cidr = cidr("192.168.42.232/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=175, cidr = cidr("192.168.42.240/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=175, cidr = cidr("192.168.42.248/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=176, cidr = cidr("192.168.43.0/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=176, cidr = cidr("192.168.43.8/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=177, cidr = cidr("192.168.43.16/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=177, cidr = cidr("192.168.43.24/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=178, cidr = cidr("192.168.43.32/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=178, cidr = cidr("192.168.43.40/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=179, cidr = cidr("192.168.43.48/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=179, cidr = cidr("192.168.43.56/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=180, cidr = cidr("192.168.43.64/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=180, cidr = cidr("192.168.43.72/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=181, cidr = cidr("192.168.43.80/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=181, cidr = cidr("192.168.43.88/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=182, cidr = cidr("192.168.43.96/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=182, cidr = cidr("192.168.43.104/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=183, cidr = cidr("192.168.43.112/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=183, cidr = cidr("192.168.43.120/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=184, cidr = cidr("192.168.43.128/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=184, cidr = cidr("192.168.43.136/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=185, cidr = cidr("192.168.43.144/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=185, cidr = cidr("192.168.43.152/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=186, cidr = cidr("192.168.43.160/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=186, cidr = cidr("192.168.43.168/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=187, cidr = cidr("192.168.43.176/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=187, cidr = cidr("192.168.43.184/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=188, cidr = cidr("192.168.43.192/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=188, cidr = cidr("192.168.43.200/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=189, cidr = cidr("192.168.43.208/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=189, cidr = cidr("192.168.43.216/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=190, cidr = cidr("192.168.43.224/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=190, cidr = cidr("192.168.43.232/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=191, cidr = cidr("192.168.43.240/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=191, cidr = cidr("192.168.43.248/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=192, cidr = cidr("192.168.44.0/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=192, cidr = cidr("192.168.44.8/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=193, cidr = cidr("192.168.44.16/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=193, cidr = cidr("192.168.44.24/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=194, cidr = cidr("192.168.44.32/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=194, cidr = cidr("192.168.44.40/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=195, cidr = cidr("192.168.44.48/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=195, cidr = cidr("192.168.44.56/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=196, cidr = cidr("192.168.44.64/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=196, cidr = cidr("192.168.44.72/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=197, cidr = cidr("192.168.44.80/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=197, cidr = cidr("192.168.44.88/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=198, cidr = cidr("192.168.44.96/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=198, cidr = cidr("192.168.44.104/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=199, cidr = cidr("192.168.44.112/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=199, cidr = cidr("192.168.44.120/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=200, cidr = cidr("192.168.44.128/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=200, cidr = cidr("192.168.44.136/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=201, cidr = cidr("192.168.44.144/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=201, cidr = cidr("192.168.44.152/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=202, cidr = cidr("192.168.44.160/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=202, cidr = cidr("192.168.44.168/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=203, cidr = cidr("192.168.44.176/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=203, cidr = cidr("192.168.44.184/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=204, cidr = cidr("192.168.44.192/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=204, cidr = cidr("192.168.44.200/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=205, cidr = cidr("192.168.44.208/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=205, cidr = cidr("192.168.44.216/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=206, cidr = cidr("192.168.44.224/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=206, cidr = cidr("192.168.44.232/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=207, cidr = cidr("192.168.44.240/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=207, cidr = cidr("192.168.44.248/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=208, cidr = cidr("192.168.45.0/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=208, cidr = cidr("192.168.45.8/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=209, cidr = cidr("192.168.45.16/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=209, cidr = cidr("192.168.45.24/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=210, cidr = cidr("192.168.45.32/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=210, cidr = cidr("192.168.45.40/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=211, cidr = cidr("192.168.45.48/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=211, cidr = cidr("192.168.45.56/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=212, cidr = cidr("192.168.45.64/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=212, cidr = cidr("192.168.45.72/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=213, cidr = cidr("192.168.45.80/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=213, cidr = cidr("192.168.45.88/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=214, cidr = cidr("192.168.45.96/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=214, cidr = cidr("192.168.45.104/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=215, cidr = cidr("192.168.45.112/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=215, cidr = cidr("192.168.45.120/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=216, cidr = cidr("192.168.45.128/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=216, cidr = cidr("192.168.45.136/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=217, cidr = cidr("192.168.45.144/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=217, cidr = cidr("192.168.45.152/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=218, cidr = cidr("192.168.45.160/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=218, cidr = cidr("192.168.45.168/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=219, cidr = cidr("192.168.45.176/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=219, cidr = cidr("192.168.45.184/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=220, cidr = cidr("192.168.45.192/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=220, cidr = cidr("192.168.45.200/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=221, cidr = cidr("192.168.45.208/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=221, cidr = cidr("192.168.45.216/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=222, cidr = cidr("192.168.45.224/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=222, cidr = cidr("192.168.45.232/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=223, cidr = cidr("192.168.45.240/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=223, cidr = cidr("192.168.45.248/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=224, cidr = cidr("192.168.46.0/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=224, cidr = cidr("192.168.46.8/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=225, cidr = cidr("192.168.46.16/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=225, cidr = cidr("192.168.46.24/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=226, cidr = cidr("192.168.46.32/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=226, cidr = cidr("192.168.46.40/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=227, cidr = cidr("192.168.46.48/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=227, cidr = cidr("192.168.46.56/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=228, cidr = cidr("192.168.46.64/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=228, cidr = cidr("192.168.46.72/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=229, cidr = cidr("192.168.46.80/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=229, cidr = cidr("192.168.46.88/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=230, cidr = cidr("192.168.46.96/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=230, cidr = cidr("192.168.46.104/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=231, cidr = cidr("192.168.46.112/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=231, cidr = cidr("192.168.46.120/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=232, cidr = cidr("192.168.46.128/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=232, cidr = cidr("192.168.46.136/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=233, cidr = cidr("192.168.46.144/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=233, cidr = cidr("192.168.46.152/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=234, cidr = cidr("192.168.46.160/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=234, cidr = cidr("192.168.46.168/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=235, cidr = cidr("192.168.46.176/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=235, cidr = cidr("192.168.46.184/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=236, cidr = cidr("192.168.46.192/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=236, cidr = cidr("192.168.46.200/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=237, cidr = cidr("192.168.46.208/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=237, cidr = cidr("192.168.46.216/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=238, cidr = cidr("192.168.46.224/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=238, cidr = cidr("192.168.46.232/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=239, cidr = cidr("192.168.46.240/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=239, cidr = cidr("192.168.46.248/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=240, cidr = cidr("192.168.47.0/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=240, cidr = cidr("192.168.47.8/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=241, cidr = cidr("192.168.47.16/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=241, cidr = cidr("192.168.47.24/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=242, cidr = cidr("192.168.47.32/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=242, cidr = cidr("192.168.47.40/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=243, cidr = cidr("192.168.47.48/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=243, cidr = cidr("192.168.47.56/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=244, cidr = cidr("192.168.47.64/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=244, cidr = cidr("192.168.47.72/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=245, cidr = cidr("192.168.47.80/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=245, cidr = cidr("192.168.47.88/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=246, cidr = cidr("192.168.47.96/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=246, cidr = cidr("192.168.47.104/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=247, cidr = cidr("192.168.47.112/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=247, cidr = cidr("192.168.47.120/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=248, cidr = cidr("192.168.47.128/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=248, cidr = cidr("192.168.47.136/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=249, cidr = cidr("192.168.47.144/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=249, cidr = cidr("192.168.47.152/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=250, cidr = cidr("192.168.47.160/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=250, cidr = cidr("192.168.47.168/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=251, cidr = cidr("192.168.47.176/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=251, cidr = cidr("192.168.47.184/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=252, cidr = cidr("192.168.47.192/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=252, cidr = cidr("192.168.47.200/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=253, cidr = cidr("192.168.47.208/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=253, cidr = cidr("192.168.47.216/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=254, cidr = cidr("192.168.47.224/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=254, cidr = cidr("192.168.47.232/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=2, gre_id=0, svlan_id=32, cvlan_id=255, cidr = cidr("192.168.47.240/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=2, gre_id=0, svlan_id=33, cvlan_id=255, cidr = cidr("192.168.47.248/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=0, cidr = cidr("192.168.48.0/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=0, cidr = cidr("192.168.48.8/29"), mac = mac("00:00:01:00:00:00"), user_id=0}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=1, cidr = cidr("192.168.48.16/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=1, cidr = cidr("192.168.48.24/29"), mac = mac("00:00:01:00:00:01"), user_id=1}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=2, cidr = cidr("192.168.48.32/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=2, cidr = cidr("192.168.48.40/29"), mac = mac("00:00:01:00:00:02"), user_id=2}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=3, cidr = cidr("192.168.48.48/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=3, cidr = cidr("192.168.48.56/29"), mac = mac("00:00:01:00:00:03"), user_id=3}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=4, cidr = cidr("192.168.48.64/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=4, cidr = cidr("192.168.48.72/29"), mac = mac("00:00:01:00:00:04"), user_id=4}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=5, cidr = cidr("192.168.48.80/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=5, cidr = cidr("192.168.48.88/29"), mac = mac("00:00:01:00:00:05"), user_id=5}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=6, cidr = cidr("192.168.48.96/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=6, cidr = cidr("192.168.48.104/29"), mac = mac("00:00:01:00:00:06"), user_id=6}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=7, cidr = cidr("192.168.48.112/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=7, cidr = cidr("192.168.48.120/29"), mac = mac("00:00:01:00:00:07"), user_id=7}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=8, cidr = cidr("192.168.48.128/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=8, cidr = cidr("192.168.48.136/29"), mac = mac("00:00:01:00:00:08"), user_id=8}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=9, cidr = cidr("192.168.48.144/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=9, cidr = cidr("192.168.48.152/29"), mac = mac("00:00:01:00:00:09"), user_id=9}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=10, cidr = cidr("192.168.48.160/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=10, cidr = cidr("192.168.48.168/29"), mac = mac("00:00:01:00:00:0a"), user_id=10}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=11, cidr = cidr("192.168.48.176/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=11, cidr = cidr("192.168.48.184/29"), mac = mac("00:00:01:00:00:0b"), user_id=11}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=12, cidr = cidr("192.168.48.192/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=12, cidr = cidr("192.168.48.200/29"), mac = mac("00:00:01:00:00:0c"), user_id=12}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=13, cidr = cidr("192.168.48.208/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=13, cidr = cidr("192.168.48.216/29"), mac = mac("00:00:01:00:00:0d"), user_id=13}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=14, cidr = cidr("192.168.48.224/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=14, cidr = cidr("192.168.48.232/29"), mac = mac("00:00:01:00:00:0e"), user_id=14}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=15, cidr = cidr("192.168.48.240/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=15, cidr = cidr("192.168.48.248/29"), mac = mac("00:00:01:00:00:0f"), user_id=15}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=16, cidr = cidr("192.168.49.0/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=16, cidr = cidr("192.168.49.8/29"), mac = mac("00:00:01:00:00:10"), user_id=16}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=17, cidr = cidr("192.168.49.16/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=17, cidr = cidr("192.168.49.24/29"), mac = mac("00:00:01:00:00:11"), user_id=17}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=18, cidr = cidr("192.168.49.32/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=18, cidr = cidr("192.168.49.40/29"), mac = mac("00:00:01:00:00:12"), user_id=18}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=19, cidr = cidr("192.168.49.48/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=19, cidr = cidr("192.168.49.56/29"), mac = mac("00:00:01:00:00:13"), user_id=19}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=20, cidr = cidr("192.168.49.64/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=20, cidr = cidr("192.168.49.72/29"), mac = mac("00:00:01:00:00:14"), user_id=20}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=21, cidr = cidr("192.168.49.80/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=21, cidr = cidr("192.168.49.88/29"), mac = mac("00:00:01:00:00:15"), user_id=21}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=22, cidr = cidr("192.168.49.96/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=22, cidr = cidr("192.168.49.104/29"), mac = mac("00:00:01:00:00:16"), user_id=22}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=23, cidr = cidr("192.168.49.112/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=23, cidr = cidr("192.168.49.120/29"), mac = mac("00:00:01:00:00:17"), user_id=23}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=24, cidr = cidr("192.168.49.128/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=24, cidr = cidr("192.168.49.136/29"), mac = mac("00:00:01:00:00:18"), user_id=24}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=25, cidr = cidr("192.168.49.144/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=25, cidr = cidr("192.168.49.152/29"), mac = mac("00:00:01:00:00:19"), user_id=25}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=26, cidr = cidr("192.168.49.160/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=26, cidr = cidr("192.168.49.168/29"), mac = mac("00:00:01:00:00:1a"), user_id=26}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=27, cidr = cidr("192.168.49.176/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=27, cidr = cidr("192.168.49.184/29"), mac = mac("00:00:01:00:00:1b"), user_id=27}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=28, cidr = cidr("192.168.49.192/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=28, cidr = cidr("192.168.49.200/29"), mac = mac("00:00:01:00:00:1c"), user_id=28}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=29, cidr = cidr("192.168.49.208/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=29, cidr = cidr("192.168.49.216/29"), mac = mac("00:00:01:00:00:1d"), user_id=29}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=30, cidr = cidr("192.168.49.224/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=30, cidr = cidr("192.168.49.232/29"), mac = mac("00:00:01:00:00:1e"), user_id=30}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=31, cidr = cidr("192.168.49.240/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=31, cidr = cidr("192.168.49.248/29"), mac = mac("00:00:01:00:00:1f"), user_id=31}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=32, cidr = cidr("192.168.50.0/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=32, cidr = cidr("192.168.50.8/29"), mac = mac("00:00:01:00:00:20"), user_id=32}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=33, cidr = cidr("192.168.50.16/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=33, cidr = cidr("192.168.50.24/29"), mac = mac("00:00:01:00:00:21"), user_id=33}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=34, cidr = cidr("192.168.50.32/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=34, cidr = cidr("192.168.50.40/29"), mac = mac("00:00:01:00:00:22"), user_id=34}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=35, cidr = cidr("192.168.50.48/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=35, cidr = cidr("192.168.50.56/29"), mac = mac("00:00:01:00:00:23"), user_id=35}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=36, cidr = cidr("192.168.50.64/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=36, cidr = cidr("192.168.50.72/29"), mac = mac("00:00:01:00:00:24"), user_id=36}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=37, cidr = cidr("192.168.50.80/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=37, cidr = cidr("192.168.50.88/29"), mac = mac("00:00:01:00:00:25"), user_id=37}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=38, cidr = cidr("192.168.50.96/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=38, cidr = cidr("192.168.50.104/29"), mac = mac("00:00:01:00:00:26"), user_id=38}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=39, cidr = cidr("192.168.50.112/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=39, cidr = cidr("192.168.50.120/29"), mac = mac("00:00:01:00:00:27"), user_id=39}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=40, cidr = cidr("192.168.50.128/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=40, cidr = cidr("192.168.50.136/29"), mac = mac("00:00:01:00:00:28"), user_id=40}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=41, cidr = cidr("192.168.50.144/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=41, cidr = cidr("192.168.50.152/29"), mac = mac("00:00:01:00:00:29"), user_id=41}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=42, cidr = cidr("192.168.50.160/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=42, cidr = cidr("192.168.50.168/29"), mac = mac("00:00:01:00:00:2a"), user_id=42}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=43, cidr = cidr("192.168.50.176/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=43, cidr = cidr("192.168.50.184/29"), mac = mac("00:00:01:00:00:2b"), user_id=43}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=44, cidr = cidr("192.168.50.192/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=44, cidr = cidr("192.168.50.200/29"), mac = mac("00:00:01:00:00:2c"), user_id=44}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=45, cidr = cidr("192.168.50.208/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=45, cidr = cidr("192.168.50.216/29"), mac = mac("00:00:01:00:00:2d"), user_id=45}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=46, cidr = cidr("192.168.50.224/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=46, cidr = cidr("192.168.50.232/29"), mac = mac("00:00:01:00:00:2e"), user_id=46}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=47, cidr = cidr("192.168.50.240/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=47, cidr = cidr("192.168.50.248/29"), mac = mac("00:00:01:00:00:2f"), user_id=47}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=48, cidr = cidr("192.168.51.0/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=48, cidr = cidr("192.168.51.8/29"), mac = mac("00:00:01:00:00:30"), user_id=48}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=49, cidr = cidr("192.168.51.16/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=49, cidr = cidr("192.168.51.24/29"), mac = mac("00:00:01:00:00:31"), user_id=49}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=50, cidr = cidr("192.168.51.32/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=50, cidr = cidr("192.168.51.40/29"), mac = mac("00:00:01:00:00:32"), user_id=50}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=51, cidr = cidr("192.168.51.48/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=51, cidr = cidr("192.168.51.56/29"), mac = mac("00:00:01:00:00:33"), user_id=51}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=52, cidr = cidr("192.168.51.64/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=52, cidr = cidr("192.168.51.72/29"), mac = mac("00:00:01:00:00:34"), user_id=52}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=53, cidr = cidr("192.168.51.80/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=53, cidr = cidr("192.168.51.88/29"), mac = mac("00:00:01:00:00:35"), user_id=53}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=54, cidr = cidr("192.168.51.96/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=54, cidr = cidr("192.168.51.104/29"), mac = mac("00:00:01:00:00:36"), user_id=54}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=55, cidr = cidr("192.168.51.112/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=55, cidr = cidr("192.168.51.120/29"), mac = mac("00:00:01:00:00:37"), user_id=55}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=56, cidr = cidr("192.168.51.128/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=56, cidr = cidr("192.168.51.136/29"), mac = mac("00:00:01:00:00:38"), user_id=56}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=57, cidr = cidr("192.168.51.144/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=57, cidr = cidr("192.168.51.152/29"), mac = mac("00:00:01:00:00:39"), user_id=57}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=58, cidr = cidr("192.168.51.160/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=58, cidr = cidr("192.168.51.168/29"), mac = mac("00:00:01:00:00:3a"), user_id=58}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=59, cidr = cidr("192.168.51.176/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=59, cidr = cidr("192.168.51.184/29"), mac = mac("00:00:01:00:00:3b"), user_id=59}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=60, cidr = cidr("192.168.51.192/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=60, cidr = cidr("192.168.51.200/29"), mac = mac("00:00:01:00:00:3c"), user_id=60}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=61, cidr = cidr("192.168.51.208/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=61, cidr = cidr("192.168.51.216/29"), mac = mac("00:00:01:00:00:3d"), user_id=61}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=62, cidr = cidr("192.168.51.224/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=62, cidr = cidr("192.168.51.232/29"), mac = mac("00:00:01:00:00:3e"), user_id=62}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=63, cidr = cidr("192.168.51.240/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=63, cidr = cidr("192.168.51.248/29"), mac = mac("00:00:01:00:00:3f"), user_id=63}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=64, cidr = cidr("192.168.52.0/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=64, cidr = cidr("192.168.52.8/29"), mac = mac("00:00:01:00:00:40"), user_id=64}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=65, cidr = cidr("192.168.52.16/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=65, cidr = cidr("192.168.52.24/29"), mac = mac("00:00:01:00:00:41"), user_id=65}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=66, cidr = cidr("192.168.52.32/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=66, cidr = cidr("192.168.52.40/29"), mac = mac("00:00:01:00:00:42"), user_id=66}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=67, cidr = cidr("192.168.52.48/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=67, cidr = cidr("192.168.52.56/29"), mac = mac("00:00:01:00:00:43"), user_id=67}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=68, cidr = cidr("192.168.52.64/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=68, cidr = cidr("192.168.52.72/29"), mac = mac("00:00:01:00:00:44"), user_id=68}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=69, cidr = cidr("192.168.52.80/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=69, cidr = cidr("192.168.52.88/29"), mac = mac("00:00:01:00:00:45"), user_id=69}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=70, cidr = cidr("192.168.52.96/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=70, cidr = cidr("192.168.52.104/29"), mac = mac("00:00:01:00:00:46"), user_id=70}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=71, cidr = cidr("192.168.52.112/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=71, cidr = cidr("192.168.52.120/29"), mac = mac("00:00:01:00:00:47"), user_id=71}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=72, cidr = cidr("192.168.52.128/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=72, cidr = cidr("192.168.52.136/29"), mac = mac("00:00:01:00:00:48"), user_id=72}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=73, cidr = cidr("192.168.52.144/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=73, cidr = cidr("192.168.52.152/29"), mac = mac("00:00:01:00:00:49"), user_id=73}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=74, cidr = cidr("192.168.52.160/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=74, cidr = cidr("192.168.52.168/29"), mac = mac("00:00:01:00:00:4a"), user_id=74}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=75, cidr = cidr("192.168.52.176/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=75, cidr = cidr("192.168.52.184/29"), mac = mac("00:00:01:00:00:4b"), user_id=75}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=76, cidr = cidr("192.168.52.192/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=76, cidr = cidr("192.168.52.200/29"), mac = mac("00:00:01:00:00:4c"), user_id=76}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=77, cidr = cidr("192.168.52.208/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=77, cidr = cidr("192.168.52.216/29"), mac = mac("00:00:01:00:00:4d"), user_id=77}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=78, cidr = cidr("192.168.52.224/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=78, cidr = cidr("192.168.52.232/29"), mac = mac("00:00:01:00:00:4e"), user_id=78}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=79, cidr = cidr("192.168.52.240/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=79, cidr = cidr("192.168.52.248/29"), mac = mac("00:00:01:00:00:4f"), user_id=79}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=80, cidr = cidr("192.168.53.0/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=80, cidr = cidr("192.168.53.8/29"), mac = mac("00:00:01:00:00:50"), user_id=80}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=81, cidr = cidr("192.168.53.16/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=81, cidr = cidr("192.168.53.24/29"), mac = mac("00:00:01:00:00:51"), user_id=81}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=82, cidr = cidr("192.168.53.32/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=82, cidr = cidr("192.168.53.40/29"), mac = mac("00:00:01:00:00:52"), user_id=82}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=83, cidr = cidr("192.168.53.48/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=83, cidr = cidr("192.168.53.56/29"), mac = mac("00:00:01:00:00:53"), user_id=83}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=84, cidr = cidr("192.168.53.64/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=84, cidr = cidr("192.168.53.72/29"), mac = mac("00:00:01:00:00:54"), user_id=84}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=85, cidr = cidr("192.168.53.80/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=85, cidr = cidr("192.168.53.88/29"), mac = mac("00:00:01:00:00:55"), user_id=85}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=86, cidr = cidr("192.168.53.96/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=86, cidr = cidr("192.168.53.104/29"), mac = mac("00:00:01:00:00:56"), user_id=86}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=87, cidr = cidr("192.168.53.112/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=87, cidr = cidr("192.168.53.120/29"), mac = mac("00:00:01:00:00:57"), user_id=87}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=88, cidr = cidr("192.168.53.128/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=88, cidr = cidr("192.168.53.136/29"), mac = mac("00:00:01:00:00:58"), user_id=88}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=89, cidr = cidr("192.168.53.144/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=89, cidr = cidr("192.168.53.152/29"), mac = mac("00:00:01:00:00:59"), user_id=89}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=90, cidr = cidr("192.168.53.160/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=90, cidr = cidr("192.168.53.168/29"), mac = mac("00:00:01:00:00:5a"), user_id=90}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=91, cidr = cidr("192.168.53.176/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=91, cidr = cidr("192.168.53.184/29"), mac = mac("00:00:01:00:00:5b"), user_id=91}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=92, cidr = cidr("192.168.53.192/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=92, cidr = cidr("192.168.53.200/29"), mac = mac("00:00:01:00:00:5c"), user_id=92}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=93, cidr = cidr("192.168.53.208/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=93, cidr = cidr("192.168.53.216/29"), mac = mac("00:00:01:00:00:5d"), user_id=93}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=94, cidr = cidr("192.168.53.224/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=94, cidr = cidr("192.168.53.232/29"), mac = mac("00:00:01:00:00:5e"), user_id=94}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=95, cidr = cidr("192.168.53.240/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=95, cidr = cidr("192.168.53.248/29"), mac = mac("00:00:01:00:00:5f"), user_id=95}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=96, cidr = cidr("192.168.54.0/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=96, cidr = cidr("192.168.54.8/29"), mac = mac("00:00:01:00:00:60"), user_id=96}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=97, cidr = cidr("192.168.54.16/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=97, cidr = cidr("192.168.54.24/29"), mac = mac("00:00:01:00:00:61"), user_id=97}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=98, cidr = cidr("192.168.54.32/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=98, cidr = cidr("192.168.54.40/29"), mac = mac("00:00:01:00:00:62"), user_id=98}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=99, cidr = cidr("192.168.54.48/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=99, cidr = cidr("192.168.54.56/29"), mac = mac("00:00:01:00:00:63"), user_id=99}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=100, cidr = cidr("192.168.54.64/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=100, cidr = cidr("192.168.54.72/29"), mac = mac("00:00:01:00:00:64"), user_id=100}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=101, cidr = cidr("192.168.54.80/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=101, cidr = cidr("192.168.54.88/29"), mac = mac("00:00:01:00:00:65"), user_id=101}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=102, cidr = cidr("192.168.54.96/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=102, cidr = cidr("192.168.54.104/29"), mac = mac("00:00:01:00:00:66"), user_id=102}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=103, cidr = cidr("192.168.54.112/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=103, cidr = cidr("192.168.54.120/29"), mac = mac("00:00:01:00:00:67"), user_id=103}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=104, cidr = cidr("192.168.54.128/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=104, cidr = cidr("192.168.54.136/29"), mac = mac("00:00:01:00:00:68"), user_id=104}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=105, cidr = cidr("192.168.54.144/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=105, cidr = cidr("192.168.54.152/29"), mac = mac("00:00:01:00:00:69"), user_id=105}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=106, cidr = cidr("192.168.54.160/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=106, cidr = cidr("192.168.54.168/29"), mac = mac("00:00:01:00:00:6a"), user_id=106}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=107, cidr = cidr("192.168.54.176/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=107, cidr = cidr("192.168.54.184/29"), mac = mac("00:00:01:00:00:6b"), user_id=107}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=108, cidr = cidr("192.168.54.192/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=108, cidr = cidr("192.168.54.200/29"), mac = mac("00:00:01:00:00:6c"), user_id=108}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=109, cidr = cidr("192.168.54.208/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=109, cidr = cidr("192.168.54.216/29"), mac = mac("00:00:01:00:00:6d"), user_id=109}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=110, cidr = cidr("192.168.54.224/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=110, cidr = cidr("192.168.54.232/29"), mac = mac("00:00:01:00:00:6e"), user_id=110}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=111, cidr = cidr("192.168.54.240/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=111, cidr = cidr("192.168.54.248/29"), mac = mac("00:00:01:00:00:6f"), user_id=111}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=112, cidr = cidr("192.168.55.0/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=112, cidr = cidr("192.168.55.8/29"), mac = mac("00:00:01:00:00:70"), user_id=112}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=113, cidr = cidr("192.168.55.16/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=113, cidr = cidr("192.168.55.24/29"), mac = mac("00:00:01:00:00:71"), user_id=113}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=114, cidr = cidr("192.168.55.32/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=114, cidr = cidr("192.168.55.40/29"), mac = mac("00:00:01:00:00:72"), user_id=114}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=115, cidr = cidr("192.168.55.48/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=115, cidr = cidr("192.168.55.56/29"), mac = mac("00:00:01:00:00:73"), user_id=115}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=116, cidr = cidr("192.168.55.64/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=116, cidr = cidr("192.168.55.72/29"), mac = mac("00:00:01:00:00:74"), user_id=116}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=117, cidr = cidr("192.168.55.80/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=117, cidr = cidr("192.168.55.88/29"), mac = mac("00:00:01:00:00:75"), user_id=117}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=118, cidr = cidr("192.168.55.96/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=118, cidr = cidr("192.168.55.104/29"), mac = mac("00:00:01:00:00:76"), user_id=118}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=119, cidr = cidr("192.168.55.112/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=119, cidr = cidr("192.168.55.120/29"), mac = mac("00:00:01:00:00:77"), user_id=119}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=120, cidr = cidr("192.168.55.128/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=120, cidr = cidr("192.168.55.136/29"), mac = mac("00:00:01:00:00:78"), user_id=120}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=121, cidr = cidr("192.168.55.144/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=121, cidr = cidr("192.168.55.152/29"), mac = mac("00:00:01:00:00:79"), user_id=121}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=122, cidr = cidr("192.168.55.160/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=122, cidr = cidr("192.168.55.168/29"), mac = mac("00:00:01:00:00:7a"), user_id=122}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=123, cidr = cidr("192.168.55.176/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=123, cidr = cidr("192.168.55.184/29"), mac = mac("00:00:01:00:00:7b"), user_id=123}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=124, cidr = cidr("192.168.55.192/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=124, cidr = cidr("192.168.55.200/29"), mac = mac("00:00:01:00:00:7c"), user_id=124}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=125, cidr = cidr("192.168.55.208/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=125, cidr = cidr("192.168.55.216/29"), mac = mac("00:00:01:00:00:7d"), user_id=125}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=126, cidr = cidr("192.168.55.224/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=126, cidr = cidr("192.168.55.232/29"), mac = mac("00:00:01:00:00:7e"), user_id=126}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=127, cidr = cidr("192.168.55.240/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=127, cidr = cidr("192.168.55.248/29"), mac = mac("00:00:01:00:00:7f"), user_id=127}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=128, cidr = cidr("192.168.56.0/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=128, cidr = cidr("192.168.56.8/29"), mac = mac("00:00:01:00:00:80"), user_id=128}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=129, cidr = cidr("192.168.56.16/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=129, cidr = cidr("192.168.56.24/29"), mac = mac("00:00:01:00:00:81"), user_id=129}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=130, cidr = cidr("192.168.56.32/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=130, cidr = cidr("192.168.56.40/29"), mac = mac("00:00:01:00:00:82"), user_id=130}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=131, cidr = cidr("192.168.56.48/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=131, cidr = cidr("192.168.56.56/29"), mac = mac("00:00:01:00:00:83"), user_id=131}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=132, cidr = cidr("192.168.56.64/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=132, cidr = cidr("192.168.56.72/29"), mac = mac("00:00:01:00:00:84"), user_id=132}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=133, cidr = cidr("192.168.56.80/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=133, cidr = cidr("192.168.56.88/29"), mac = mac("00:00:01:00:00:85"), user_id=133}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=134, cidr = cidr("192.168.56.96/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=134, cidr = cidr("192.168.56.104/29"), mac = mac("00:00:01:00:00:86"), user_id=134}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=135, cidr = cidr("192.168.56.112/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=135, cidr = cidr("192.168.56.120/29"), mac = mac("00:00:01:00:00:87"), user_id=135}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=136, cidr = cidr("192.168.56.128/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=136, cidr = cidr("192.168.56.136/29"), mac = mac("00:00:01:00:00:88"), user_id=136}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=137, cidr = cidr("192.168.56.144/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=137, cidr = cidr("192.168.56.152/29"), mac = mac("00:00:01:00:00:89"), user_id=137}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=138, cidr = cidr("192.168.56.160/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=138, cidr = cidr("192.168.56.168/29"), mac = mac("00:00:01:00:00:8a"), user_id=138}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=139, cidr = cidr("192.168.56.176/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=139, cidr = cidr("192.168.56.184/29"), mac = mac("00:00:01:00:00:8b"), user_id=139}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=140, cidr = cidr("192.168.56.192/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=140, cidr = cidr("192.168.56.200/29"), mac = mac("00:00:01:00:00:8c"), user_id=140}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=141, cidr = cidr("192.168.56.208/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=141, cidr = cidr("192.168.56.216/29"), mac = mac("00:00:01:00:00:8d"), user_id=141}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=142, cidr = cidr("192.168.56.224/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=142, cidr = cidr("192.168.56.232/29"), mac = mac("00:00:01:00:00:8e"), user_id=142}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=143, cidr = cidr("192.168.56.240/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=143, cidr = cidr("192.168.56.248/29"), mac = mac("00:00:01:00:00:8f"), user_id=143}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=144, cidr = cidr("192.168.57.0/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=144, cidr = cidr("192.168.57.8/29"), mac = mac("00:00:01:00:00:90"), user_id=144}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=145, cidr = cidr("192.168.57.16/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=145, cidr = cidr("192.168.57.24/29"), mac = mac("00:00:01:00:00:91"), user_id=145}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=146, cidr = cidr("192.168.57.32/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=146, cidr = cidr("192.168.57.40/29"), mac = mac("00:00:01:00:00:92"), user_id=146}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=147, cidr = cidr("192.168.57.48/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=147, cidr = cidr("192.168.57.56/29"), mac = mac("00:00:01:00:00:93"), user_id=147}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=148, cidr = cidr("192.168.57.64/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=148, cidr = cidr("192.168.57.72/29"), mac = mac("00:00:01:00:00:94"), user_id=148}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=149, cidr = cidr("192.168.57.80/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=149, cidr = cidr("192.168.57.88/29"), mac = mac("00:00:01:00:00:95"), user_id=149}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=150, cidr = cidr("192.168.57.96/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=150, cidr = cidr("192.168.57.104/29"), mac = mac("00:00:01:00:00:96"), user_id=150}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=151, cidr = cidr("192.168.57.112/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=151, cidr = cidr("192.168.57.120/29"), mac = mac("00:00:01:00:00:97"), user_id=151}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=152, cidr = cidr("192.168.57.128/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=152, cidr = cidr("192.168.57.136/29"), mac = mac("00:00:01:00:00:98"), user_id=152}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=153, cidr = cidr("192.168.57.144/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=153, cidr = cidr("192.168.57.152/29"), mac = mac("00:00:01:00:00:99"), user_id=153}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=154, cidr = cidr("192.168.57.160/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=154, cidr = cidr("192.168.57.168/29"), mac = mac("00:00:01:00:00:9a"), user_id=154}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=155, cidr = cidr("192.168.57.176/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=155, cidr = cidr("192.168.57.184/29"), mac = mac("00:00:01:00:00:9b"), user_id=155}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=156, cidr = cidr("192.168.57.192/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=156, cidr = cidr("192.168.57.200/29"), mac = mac("00:00:01:00:00:9c"), user_id=156}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=157, cidr = cidr("192.168.57.208/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=157, cidr = cidr("192.168.57.216/29"), mac = mac("00:00:01:00:00:9d"), user_id=157}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=158, cidr = cidr("192.168.57.224/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=158, cidr = cidr("192.168.57.232/29"), mac = mac("00:00:01:00:00:9e"), user_id=158}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=159, cidr = cidr("192.168.57.240/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=159, cidr = cidr("192.168.57.248/29"), mac = mac("00:00:01:00:00:9f"), user_id=159}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=160, cidr = cidr("192.168.58.0/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=160, cidr = cidr("192.168.58.8/29"), mac = mac("00:00:01:00:00:a0"), user_id=160}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=161, cidr = cidr("192.168.58.16/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=161, cidr = cidr("192.168.58.24/29"), mac = mac("00:00:01:00:00:a1"), user_id=161}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=162, cidr = cidr("192.168.58.32/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=162, cidr = cidr("192.168.58.40/29"), mac = mac("00:00:01:00:00:a2"), user_id=162}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=163, cidr = cidr("192.168.58.48/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=163, cidr = cidr("192.168.58.56/29"), mac = mac("00:00:01:00:00:a3"), user_id=163}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=164, cidr = cidr("192.168.58.64/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=164, cidr = cidr("192.168.58.72/29"), mac = mac("00:00:01:00:00:a4"), user_id=164}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=165, cidr = cidr("192.168.58.80/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=165, cidr = cidr("192.168.58.88/29"), mac = mac("00:00:01:00:00:a5"), user_id=165}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=166, cidr = cidr("192.168.58.96/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=166, cidr = cidr("192.168.58.104/29"), mac = mac("00:00:01:00:00:a6"), user_id=166}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=167, cidr = cidr("192.168.58.112/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=167, cidr = cidr("192.168.58.120/29"), mac = mac("00:00:01:00:00:a7"), user_id=167}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=168, cidr = cidr("192.168.58.128/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=168, cidr = cidr("192.168.58.136/29"), mac = mac("00:00:01:00:00:a8"), user_id=168}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=169, cidr = cidr("192.168.58.144/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=169, cidr = cidr("192.168.58.152/29"), mac = mac("00:00:01:00:00:a9"), user_id=169}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=170, cidr = cidr("192.168.58.160/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=170, cidr = cidr("192.168.58.168/29"), mac = mac("00:00:01:00:00:aa"), user_id=170}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=171, cidr = cidr("192.168.58.176/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=171, cidr = cidr("192.168.58.184/29"), mac = mac("00:00:01:00:00:ab"), user_id=171}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=172, cidr = cidr("192.168.58.192/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=172, cidr = cidr("192.168.58.200/29"), mac = mac("00:00:01:00:00:ac"), user_id=172}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=173, cidr = cidr("192.168.58.208/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=173, cidr = cidr("192.168.58.216/29"), mac = mac("00:00:01:00:00:ad"), user_id=173}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=174, cidr = cidr("192.168.58.224/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=174, cidr = cidr("192.168.58.232/29"), mac = mac("00:00:01:00:00:ae"), user_id=174}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=175, cidr = cidr("192.168.58.240/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=175, cidr = cidr("192.168.58.248/29"), mac = mac("00:00:01:00:00:af"), user_id=175}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=176, cidr = cidr("192.168.59.0/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=176, cidr = cidr("192.168.59.8/29"), mac = mac("00:00:01:00:00:b0"), user_id=176}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=177, cidr = cidr("192.168.59.16/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=177, cidr = cidr("192.168.59.24/29"), mac = mac("00:00:01:00:00:b1"), user_id=177}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=178, cidr = cidr("192.168.59.32/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=178, cidr = cidr("192.168.59.40/29"), mac = mac("00:00:01:00:00:b2"), user_id=178}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=179, cidr = cidr("192.168.59.48/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=179, cidr = cidr("192.168.59.56/29"), mac = mac("00:00:01:00:00:b3"), user_id=179}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=180, cidr = cidr("192.168.59.64/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=180, cidr = cidr("192.168.59.72/29"), mac = mac("00:00:01:00:00:b4"), user_id=180}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=181, cidr = cidr("192.168.59.80/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=181, cidr = cidr("192.168.59.88/29"), mac = mac("00:00:01:00:00:b5"), user_id=181}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=182, cidr = cidr("192.168.59.96/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=182, cidr = cidr("192.168.59.104/29"), mac = mac("00:00:01:00:00:b6"), user_id=182}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=183, cidr = cidr("192.168.59.112/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=183, cidr = cidr("192.168.59.120/29"), mac = mac("00:00:01:00:00:b7"), user_id=183}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=184, cidr = cidr("192.168.59.128/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=184, cidr = cidr("192.168.59.136/29"), mac = mac("00:00:01:00:00:b8"), user_id=184}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=185, cidr = cidr("192.168.59.144/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=185, cidr = cidr("192.168.59.152/29"), mac = mac("00:00:01:00:00:b9"), user_id=185}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=186, cidr = cidr("192.168.59.160/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=186, cidr = cidr("192.168.59.168/29"), mac = mac("00:00:01:00:00:ba"), user_id=186}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=187, cidr = cidr("192.168.59.176/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=187, cidr = cidr("192.168.59.184/29"), mac = mac("00:00:01:00:00:bb"), user_id=187}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=188, cidr = cidr("192.168.59.192/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=188, cidr = cidr("192.168.59.200/29"), mac = mac("00:00:01:00:00:bc"), user_id=188}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=189, cidr = cidr("192.168.59.208/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=189, cidr = cidr("192.168.59.216/29"), mac = mac("00:00:01:00:00:bd"), user_id=189}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=190, cidr = cidr("192.168.59.224/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=190, cidr = cidr("192.168.59.232/29"), mac = mac("00:00:01:00:00:be"), user_id=190}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=191, cidr = cidr("192.168.59.240/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=191, cidr = cidr("192.168.59.248/29"), mac = mac("00:00:01:00:00:bf"), user_id=191}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=192, cidr = cidr("192.168.60.0/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=192, cidr = cidr("192.168.60.8/29"), mac = mac("00:00:01:00:00:c0"), user_id=192}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=193, cidr = cidr("192.168.60.16/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=193, cidr = cidr("192.168.60.24/29"), mac = mac("00:00:01:00:00:c1"), user_id=193}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=194, cidr = cidr("192.168.60.32/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=194, cidr = cidr("192.168.60.40/29"), mac = mac("00:00:01:00:00:c2"), user_id=194}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=195, cidr = cidr("192.168.60.48/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=195, cidr = cidr("192.168.60.56/29"), mac = mac("00:00:01:00:00:c3"), user_id=195}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=196, cidr = cidr("192.168.60.64/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=196, cidr = cidr("192.168.60.72/29"), mac = mac("00:00:01:00:00:c4"), user_id=196}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=197, cidr = cidr("192.168.60.80/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=197, cidr = cidr("192.168.60.88/29"), mac = mac("00:00:01:00:00:c5"), user_id=197}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=198, cidr = cidr("192.168.60.96/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=198, cidr = cidr("192.168.60.104/29"), mac = mac("00:00:01:00:00:c6"), user_id=198}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=199, cidr = cidr("192.168.60.112/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=199, cidr = cidr("192.168.60.120/29"), mac = mac("00:00:01:00:00:c7"), user_id=199}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=200, cidr = cidr("192.168.60.128/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=200, cidr = cidr("192.168.60.136/29"), mac = mac("00:00:01:00:00:c8"), user_id=200}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=201, cidr = cidr("192.168.60.144/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=201, cidr = cidr("192.168.60.152/29"), mac = mac("00:00:01:00:00:c9"), user_id=201}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=202, cidr = cidr("192.168.60.160/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=202, cidr = cidr("192.168.60.168/29"), mac = mac("00:00:01:00:00:ca"), user_id=202}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=203, cidr = cidr("192.168.60.176/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=203, cidr = cidr("192.168.60.184/29"), mac = mac("00:00:01:00:00:cb"), user_id=203}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=204, cidr = cidr("192.168.60.192/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=204, cidr = cidr("192.168.60.200/29"), mac = mac("00:00:01:00:00:cc"), user_id=204}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=205, cidr = cidr("192.168.60.208/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=205, cidr = cidr("192.168.60.216/29"), mac = mac("00:00:01:00:00:cd"), user_id=205}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=206, cidr = cidr("192.168.60.224/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=206, cidr = cidr("192.168.60.232/29"), mac = mac("00:00:01:00:00:ce"), user_id=206}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=207, cidr = cidr("192.168.60.240/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=207, cidr = cidr("192.168.60.248/29"), mac = mac("00:00:01:00:00:cf"), user_id=207}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=208, cidr = cidr("192.168.61.0/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=208, cidr = cidr("192.168.61.8/29"), mac = mac("00:00:01:00:00:d0"), user_id=208}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=209, cidr = cidr("192.168.61.16/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=209, cidr = cidr("192.168.61.24/29"), mac = mac("00:00:01:00:00:d1"), user_id=209}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=210, cidr = cidr("192.168.61.32/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=210, cidr = cidr("192.168.61.40/29"), mac = mac("00:00:01:00:00:d2"), user_id=210}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=211, cidr = cidr("192.168.61.48/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=211, cidr = cidr("192.168.61.56/29"), mac = mac("00:00:01:00:00:d3"), user_id=211}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=212, cidr = cidr("192.168.61.64/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=212, cidr = cidr("192.168.61.72/29"), mac = mac("00:00:01:00:00:d4"), user_id=212}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=213, cidr = cidr("192.168.61.80/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=213, cidr = cidr("192.168.61.88/29"), mac = mac("00:00:01:00:00:d5"), user_id=213}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=214, cidr = cidr("192.168.61.96/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=214, cidr = cidr("192.168.61.104/29"), mac = mac("00:00:01:00:00:d6"), user_id=214}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=215, cidr = cidr("192.168.61.112/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=215, cidr = cidr("192.168.61.120/29"), mac = mac("00:00:01:00:00:d7"), user_id=215}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=216, cidr = cidr("192.168.61.128/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=216, cidr = cidr("192.168.61.136/29"), mac = mac("00:00:01:00:00:d8"), user_id=216}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=217, cidr = cidr("192.168.61.144/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=217, cidr = cidr("192.168.61.152/29"), mac = mac("00:00:01:00:00:d9"), user_id=217}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=218, cidr = cidr("192.168.61.160/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=218, cidr = cidr("192.168.61.168/29"), mac = mac("00:00:01:00:00:da"), user_id=218}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=219, cidr = cidr("192.168.61.176/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=219, cidr = cidr("192.168.61.184/29"), mac = mac("00:00:01:00:00:db"), user_id=219}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=220, cidr = cidr("192.168.61.192/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=220, cidr = cidr("192.168.61.200/29"), mac = mac("00:00:01:00:00:dc"), user_id=220}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=221, cidr = cidr("192.168.61.208/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=221, cidr = cidr("192.168.61.216/29"), mac = mac("00:00:01:00:00:dd"), user_id=221}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=222, cidr = cidr("192.168.61.224/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=222, cidr = cidr("192.168.61.232/29"), mac = mac("00:00:01:00:00:de"), user_id=222}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=223, cidr = cidr("192.168.61.240/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=223, cidr = cidr("192.168.61.248/29"), mac = mac("00:00:01:00:00:df"), user_id=223}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=224, cidr = cidr("192.168.62.0/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=224, cidr = cidr("192.168.62.8/29"), mac = mac("00:00:01:00:00:e0"), user_id=224}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=225, cidr = cidr("192.168.62.16/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=225, cidr = cidr("192.168.62.24/29"), mac = mac("00:00:01:00:00:e1"), user_id=225}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=226, cidr = cidr("192.168.62.32/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=226, cidr = cidr("192.168.62.40/29"), mac = mac("00:00:01:00:00:e2"), user_id=226}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=227, cidr = cidr("192.168.62.48/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=227, cidr = cidr("192.168.62.56/29"), mac = mac("00:00:01:00:00:e3"), user_id=227}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=228, cidr = cidr("192.168.62.64/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=228, cidr = cidr("192.168.62.72/29"), mac = mac("00:00:01:00:00:e4"), user_id=228}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=229, cidr = cidr("192.168.62.80/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=229, cidr = cidr("192.168.62.88/29"), mac = mac("00:00:01:00:00:e5"), user_id=229}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=230, cidr = cidr("192.168.62.96/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=230, cidr = cidr("192.168.62.104/29"), mac = mac("00:00:01:00:00:e6"), user_id=230}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=231, cidr = cidr("192.168.62.112/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=231, cidr = cidr("192.168.62.120/29"), mac = mac("00:00:01:00:00:e7"), user_id=231}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=232, cidr = cidr("192.168.62.128/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=232, cidr = cidr("192.168.62.136/29"), mac = mac("00:00:01:00:00:e8"), user_id=232}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=233, cidr = cidr("192.168.62.144/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=233, cidr = cidr("192.168.62.152/29"), mac = mac("00:00:01:00:00:e9"), user_id=233}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=234, cidr = cidr("192.168.62.160/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=234, cidr = cidr("192.168.62.168/29"), mac = mac("00:00:01:00:00:ea"), user_id=234}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=235, cidr = cidr("192.168.62.176/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=235, cidr = cidr("192.168.62.184/29"), mac = mac("00:00:01:00:00:eb"), user_id=235}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=236, cidr = cidr("192.168.62.192/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=236, cidr = cidr("192.168.62.200/29"), mac = mac("00:00:01:00:00:ec"), user_id=236}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=237, cidr = cidr("192.168.62.208/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=237, cidr = cidr("192.168.62.216/29"), mac = mac("00:00:01:00:00:ed"), user_id=237}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=238, cidr = cidr("192.168.62.224/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=238, cidr = cidr("192.168.62.232/29"), mac = mac("00:00:01:00:00:ee"), user_id=238}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=239, cidr = cidr("192.168.62.240/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=239, cidr = cidr("192.168.62.248/29"), mac = mac("00:00:01:00:00:ef"), user_id=239}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=240, cidr = cidr("192.168.63.0/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=240, cidr = cidr("192.168.63.8/29"), mac = mac("00:00:01:00:00:f0"), user_id=240}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=241, cidr = cidr("192.168.63.16/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=241, cidr = cidr("192.168.63.24/29"), mac = mac("00:00:01:00:00:f1"), user_id=241}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=242, cidr = cidr("192.168.63.32/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=242, cidr = cidr("192.168.63.40/29"), mac = mac("00:00:01:00:00:f2"), user_id=242}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=243, cidr = cidr("192.168.63.48/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=243, cidr = cidr("192.168.63.56/29"), mac = mac("00:00:01:00:00:f3"), user_id=243}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=244, cidr = cidr("192.168.63.64/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=244, cidr = cidr("192.168.63.72/29"), mac = mac("00:00:01:00:00:f4"), user_id=244}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=245, cidr = cidr("192.168.63.80/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=245, cidr = cidr("192.168.63.88/29"), mac = mac("00:00:01:00:00:f5"), user_id=245}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=246, cidr = cidr("192.168.63.96/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=246, cidr = cidr("192.168.63.104/29"), mac = mac("00:00:01:00:00:f6"), user_id=246}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=247, cidr = cidr("192.168.63.112/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=247, cidr = cidr("192.168.63.120/29"), mac = mac("00:00:01:00:00:f7"), user_id=247}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=248, cidr = cidr("192.168.63.128/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=248, cidr = cidr("192.168.63.136/29"), mac = mac("00:00:01:00:00:f8"), user_id=248}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=249, cidr = cidr("192.168.63.144/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=249, cidr = cidr("192.168.63.152/29"), mac = mac("00:00:01:00:00:f9"), user_id=249}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=250, cidr = cidr("192.168.63.160/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=250, cidr = cidr("192.168.63.168/29"), mac = mac("00:00:01:00:00:fa"), user_id=250}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=251, cidr = cidr("192.168.63.176/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=251, cidr = cidr("192.168.63.184/29"), mac = mac("00:00:01:00:00:fb"), user_id=251}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=252, cidr = cidr("192.168.63.192/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=252, cidr = cidr("192.168.63.200/29"), mac = mac("00:00:01:00:00:fc"), user_id=252}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=253, cidr = cidr("192.168.63.208/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=253, cidr = cidr("192.168.63.216/29"), mac = mac("00:00:01:00:00:fd"), user_id=253}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=254, cidr = cidr("192.168.63.224/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=254, cidr = cidr("192.168.63.232/29"), mac = mac("00:00:01:00:00:fe"), user_id=254}, + {dest_id=3, gre_id=0, svlan_id=48, cvlan_id=255, cidr = cidr("192.168.63.240/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, + {dest_id=3, gre_id=0, svlan_id=49, cvlan_id=255, cidr = cidr("192.168.63.248/29"), mac = mac("00:00:01:00:00:ff"), user_id=255}, +} diff --git a/VNFs/DPPD-PROX/config/cpe_table_short.lua b/VNFs/DPPD-PROX/config/cpe_table_short.lua new file mode 100644 index 00000000..c2314897 --- /dev/null +++ b/VNFs/DPPD-PROX/config/cpe_table_short.lua @@ -0,0 +1,32 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +t2={} +svlan_id=0 +ip1=2^24*192 + 2^16*168 + 2^8*0 + 0; +for id1=0,3,1 do + for cvlan_id=0,255,1 do + mac_s=string.format("00:00:01:00:00:%02x", cvlan_id); + table.insert(t2,{dest_id=id1, gre_id=0, svlan_id=svlan_id, cvlan_id=cvlan_id, cidr = {ip=ip(ip1),depth=29}, mac = mac(mac_s), user_id=cvlan_id}); + ip1=ip1+8 + table.insert(t2,{dest_id=id1, gre_id=0, svlan_id=svlan_id+1, cvlan_id=cvlan_id, cidr = {ip=ip(ip1),depth=29}, mac = mac(mac_s), user_id=cvlan_id}); + ip1=ip1+8 + end + svlan_id=svlan_id+16 +end + +return t; + diff --git a/VNFs/DPPD-PROX/config/dscp.lua b/VNFs/DPPD-PROX/config/dscp.lua new file mode 100644 index 00000000..ff994b16 --- /dev/null +++ b/VNFs/DPPD-PROX/config/dscp.lua @@ -0,0 +1,82 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +return { + {dscp = 0, tc = 0, queue = 0}, + {dscp = 1, tc = 0, queue = 1}, + {dscp = 2, tc = 0, queue = 2}, + {dscp = 3, tc = 0, queue = 3}, + {dscp = 4, tc = 1, queue = 0}, + {dscp = 5, tc = 1, queue = 1}, + {dscp = 6, tc = 1, queue = 2}, + {dscp = 7, tc = 1, queue = 3}, + {dscp = 8, tc = 2, queue = 0}, + {dscp = 9, tc = 2, queue = 1}, + {dscp = 10, tc = 2, queue = 2}, + {dscp = 11, tc = 2, queue = 3}, + {dscp = 12, tc = 3, queue = 0}, + {dscp = 13, tc = 3, queue = 1}, + {dscp = 14, tc = 3, queue = 2}, + {dscp = 15, tc = 3, queue = 3}, + {dscp = 16, tc = 0, queue = 0}, + {dscp = 17, tc = 0, queue = 1}, + {dscp = 18, tc = 0, queue = 2}, + {dscp = 19, tc = 0, queue = 3}, + {dscp = 20, tc = 1, queue = 0}, + {dscp = 21, tc = 1, queue = 1}, + {dscp = 22, tc = 1, queue = 2}, + {dscp = 23, tc = 1, queue = 3}, + {dscp = 24, tc = 2, queue = 0}, + {dscp = 25, tc = 2, queue = 1}, + {dscp = 26, tc = 2, queue = 2}, + {dscp = 27, tc = 2, queue = 3}, + {dscp = 28, tc = 3, queue = 0}, + {dscp = 29, tc = 3, queue = 1}, + {dscp = 30, tc = 3, queue = 2}, + {dscp = 31, tc = 3, queue = 3}, + {dscp = 32, tc = 0, queue = 0}, + {dscp = 33, tc = 0, queue = 1}, + {dscp = 34, tc = 0, queue = 2}, + {dscp = 35, tc = 0, queue = 3}, + {dscp = 36, tc = 1, queue = 0}, + {dscp = 37, tc = 1, queue = 1}, + {dscp = 38, tc = 1, queue = 2}, + {dscp = 39, tc = 1, queue = 3}, + {dscp = 40, tc = 2, queue = 0}, + {dscp = 41, tc = 2, queue = 1}, + {dscp = 42, tc = 2, queue = 2}, + {dscp = 43, tc = 2, queue = 3}, + {dscp = 44, tc = 3, queue = 0}, + {dscp = 45, tc = 3, queue = 1}, + {dscp = 46, tc = 3, queue = 2}, + {dscp = 47, tc = 3, queue = 3}, + {dscp = 48, tc = 0, queue = 0}, + {dscp = 49, tc = 0, queue = 1}, + {dscp = 50, tc = 0, queue = 2}, + {dscp = 51, tc = 0, queue = 3}, + {dscp = 52, tc = 1, queue = 0}, + {dscp = 53, tc = 1, queue = 1}, + {dscp = 54, tc = 1, queue = 2}, + {dscp = 55, tc = 1, queue = 3}, + {dscp = 56, tc = 2, queue = 0}, + {dscp = 57, tc = 2, queue = 1}, + {dscp = 58, tc = 2, queue = 2}, + {dscp = 59, tc = 2, queue = 3}, + {dscp = 60, tc = 3, queue = 0}, + {dscp = 61, tc = 3, queue = 1}, + {dscp = 62, tc = 3, queue = 2}, + {dscp = 63, tc = 3, queue = 3}, +} diff --git a/VNFs/DPPD-PROX/config/dscp2.lua b/VNFs/DPPD-PROX/config/dscp2.lua new file mode 100644 index 00000000..0bc044a8 --- /dev/null +++ b/VNFs/DPPD-PROX/config/dscp2.lua @@ -0,0 +1,22 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local dscp = {} +for i = 1,2^6 do + dscp[i] = {dscp = i, tc = 0, queue = 0} +end + +return dscp; diff --git a/VNFs/DPPD-PROX/config/ip6_tun_bind.lua b/VNFs/DPPD-PROX/config/ip6_tun_bind.lua new file mode 100644 index 00000000..75910048 --- /dev/null +++ b/VNFs/DPPD-PROX/config/ip6_tun_bind.lua @@ -0,0 +1,25 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- Bindings for lwaftr: lwB4 IPv6 address, next hop MAC address +-- towards lwB4, IPv4 Public address, IPv4 Public Port Set + +return { + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0000"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4608}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0001"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4672}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0002"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4736}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0003"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4800}, +} diff --git a/VNFs/DPPD-PROX/config/ipv4-2.lua b/VNFs/DPPD-PROX/config/ipv4-2.lua new file mode 100644 index 00000000..0283ed48 --- /dev/null +++ b/VNFs/DPPD-PROX/config/ipv4-2.lua @@ -0,0 +1,109 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local lpm4 = {} +lpm4.next_hops = { + {id = 0, port_id = 0, ip = ip("1.1.1.1"), mac = mac("00:00:00:00:00:01"), mpls = 0x112}, + {id = 1, port_id = 1, ip = ip("2.1.1.1"), mac = mac("00:00:00:00:00:02"), mpls = 0x212}, + {id = 2, port_id = 0, ip = ip("3.1.1.1"), mac = mac("00:00:00:00:00:03"), mpls = 0x312}, + {id = 3, port_id = 1, ip = ip("4.1.1.1"), mac = mac("00:00:00:00:00:04"), mpls = 0x412}, + {id = 4, port_id = 0, ip = ip("5.1.1.1"), mac = mac("00:00:00:00:00:05"), mpls = 0x512}, + {id = 5, port_id = 1, ip = ip("6.1.1.1"), mac = mac("00:00:00:00:00:06"), mpls = 0x612}, + {id = 6, port_id = 0, ip = ip("7.1.1.1"), mac = mac("00:00:00:00:00:07"), mpls = 0x712}, + {id = 7, port_id = 1, ip = ip("8.1.1.1"), mac = mac("00:00:00:00:00:08"), mpls = 0x812}, + {id = 8, port_id = 0, ip = ip("9.1.1.1"), mac = mac("00:00:00:00:00:09"), mpls = 0x912}, + {id = 9, port_id = 1, ip = ip("10.1.1.1"), mac = mac("00:00:00:00:00:10"), mpls = 0x1012}, + {id = 10, port_id = 0, ip = ip("11.1.1.1"), mac = mac("00:00:00:00:00:11"), mpls = 0x1112}, + {id = 11, port_id = 1, ip = ip("12.1.1.1"), mac = mac("00:00:00:00:00:12"), mpls = 0x1212}, + {id = 12, port_id = 0, ip = ip("13.1.1.1"), mac = mac("00:00:00:00:00:13"), mpls = 0x1312}, + {id = 13, port_id = 1, ip = ip("14.1.1.1"), mac = mac("00:00:00:00:00:14"), mpls = 0x1412}, + {id = 14, port_id = 0, ip = ip("15.1.1.1"), mac = mac("00:00:00:00:00:15"), mpls = 0x1512}, + {id = 15, port_id = 1, ip = ip("16.1.1.1"), mac = mac("00:00:00:00:00:16"), mpls = 0x1612}, + {id = 16, port_id = 0, ip = ip("17.1.1.1"), mac = mac("00:00:00:00:00:17"), mpls = 0x1712}, + {id = 17, port_id = 1, ip = ip("18.1.1.1"), mac = mac("00:00:00:00:00:18"), mpls = 0x1812}, + {id = 18, port_id = 0, ip = ip("19.1.1.1"), mac = mac("00:00:00:00:00:19"), mpls = 0x1912}, + {id = 19, port_id = 1, ip = ip("20.1.1.1"), mac = mac("00:00:00:00:00:20"), mpls = 0x2012}, + {id = 20, port_id = 0, ip = ip("21.1.1.1"), mac = mac("00:00:00:00:00:21"), mpls = 0x2112}, + {id = 21, port_id = 1, ip = ip("22.1.1.1"), mac = mac("00:00:00:00:00:22"), mpls = 0x2212}, + {id = 22, port_id = 0, ip = ip("23.1.1.1"), mac = mac("00:00:00:00:00:23"), mpls = 0x2312}, + {id = 23, port_id = 1, ip = ip("24.1.1.1"), mac = mac("00:00:00:00:00:24"), mpls = 0x2412}, + {id = 24, port_id = 0, ip = ip("25.1.1.1"), mac = mac("00:00:00:00:00:25"), mpls = 0x2512}, + {id = 25, port_id = 1, ip = ip("26.1.1.1"), mac = mac("00:00:00:00:00:26"), mpls = 0x2612}, + {id = 26, port_id = 0, ip = ip("27.1.1.1"), mac = mac("00:00:00:00:00:27"), mpls = 0x2712}, + {id = 27, port_id = 1, ip = ip("28.1.1.1"), mac = mac("00:00:00:00:00:28"), mpls = 0x2812}, + {id = 28, port_id = 0, ip = ip("29.1.1.1"), mac = mac("00:00:00:00:00:29"), mpls = 0x2912}, + {id = 29, port_id = 1, ip = ip("30.1.1.1"), mac = mac("00:00:00:00:00:30"), mpls = 0x3012}, + {id = 30, port_id = 0, ip = ip("31.1.1.1"), mac = mac("00:00:00:00:00:31"), mpls = 0x3112}, + {id = 31, port_id = 1, ip = ip("32.1.1.1"), mac = mac("00:00:00:00:00:32"), mpls = 0x3212}, + {id = 32, port_id = 0, ip = ip("33.1.1.1"), mac = mac("00:00:00:00:00:33"), mpls = 0x3312}, + {id = 33, port_id = 1, ip = ip("34.1.1.1"), mac = mac("00:00:00:00:00:34"), mpls = 0x3412}, + {id = 34, port_id = 0, ip = ip("35.1.1.1"), mac = mac("00:00:00:00:00:35"), mpls = 0x3512}, + {id = 35, port_id = 1, ip = ip("36.1.1.1"), mac = mac("00:00:00:00:00:36"), mpls = 0x3612}, + {id = 36, port_id = 0, ip = ip("37.1.1.1"), mac = mac("00:00:00:00:00:37"), mpls = 0x3712}, + {id = 37, port_id = 1, ip = ip("38.1.1.1"), mac = mac("00:00:00:00:00:38"), mpls = 0x3812}, + {id = 38, port_id = 0, ip = ip("39.1.1.1"), mac = mac("00:00:00:00:00:39"), mpls = 0x3912}, + {id = 39, port_id = 1, ip = ip("40.1.1.1"), mac = mac("00:00:00:00:00:40"), mpls = 0x4012}, + {id = 40, port_id = 0, ip = ip("41.1.1.1"), mac = mac("00:00:00:00:00:41"), mpls = 0x4112}, + {id = 41, port_id = 1, ip = ip("42.1.1.1"), mac = mac("00:00:00:00:00:42"), mpls = 0x4212}, + {id = 42, port_id = 0, ip = ip("43.1.1.1"), mac = mac("00:00:00:00:00:43"), mpls = 0x4312}, + {id = 43, port_id = 1, ip = ip("44.1.1.1"), mac = mac("00:00:00:00:00:44"), mpls = 0x4412}, + {id = 44, port_id = 0, ip = ip("45.1.1.1"), mac = mac("00:00:00:00:00:45"), mpls = 0x4512}, + {id = 45, port_id = 1, ip = ip("46.1.1.1"), mac = mac("00:00:00:00:00:46"), mpls = 0x4612}, + {id = 46, port_id = 0, ip = ip("47.1.1.1"), mac = mac("00:00:00:00:00:47"), mpls = 0x4712}, + {id = 47, port_id = 1, ip = ip("48.1.1.1"), mac = mac("00:00:00:00:00:48"), mpls = 0x4812}, + {id = 48, port_id = 0, ip = ip("49.1.1.1"), mac = mac("00:00:00:00:00:49"), mpls = 0x4912}, + {id = 49, port_id = 1, ip = ip("50.1.1.1"), mac = mac("00:00:00:00:00:50"), mpls = 0x5012}, + {id = 50, port_id = 0, ip = ip("51.1.1.1"), mac = mac("00:00:00:00:00:51"), mpls = 0x5112}, + {id = 51, port_id = 1, ip = ip("52.1.1.1"), mac = mac("00:00:00:00:00:52"), mpls = 0x5212}, + {id = 52, port_id = 0, ip = ip("53.1.1.1"), mac = mac("00:00:00:00:00:53"), mpls = 0x5312}, + {id = 53, port_id = 1, ip = ip("54.1.1.1"), mac = mac("00:00:00:00:00:54"), mpls = 0x5412}, + {id = 54, port_id = 0, ip = ip("55.1.1.1"), mac = mac("00:00:00:00:00:55"), mpls = 0x5512}, + {id = 55, port_id = 1, ip = ip("56.1.1.1"), mac = mac("00:00:00:00:00:56"), mpls = 0x5612}, + {id = 56, port_id = 0, ip = ip("57.1.1.1"), mac = mac("00:00:00:00:00:57"), mpls = 0x5712}, + {id = 57, port_id = 1, ip = ip("58.1.1.1"), mac = mac("00:00:00:00:00:58"), mpls = 0x5812}, + {id = 58, port_id = 0, ip = ip("59.1.1.1"), mac = mac("00:00:00:00:00:59"), mpls = 0x5912}, + {id = 59, port_id = 1, ip = ip("60.1.1.1"), mac = mac("00:00:00:00:00:60"), mpls = 0x6012}, + {id = 60, port_id = 0, ip = ip("61.1.1.1"), mac = mac("00:00:00:00:00:61"), mpls = 0x6112}, + {id = 61, port_id = 1, ip = ip("62.1.1.1"), mac = mac("00:00:00:00:00:62"), mpls = 0x6212}, + {id = 62, port_id = 0, ip = ip("63.1.1.1"), mac = mac("00:00:00:00:00:63"), mpls = 0x6312}, + {id = 63, port_id = 1, ip = ip("64.1.1.1"), mac = mac("00:00:00:00:00:64"), mpls = 0x6412}, +} + +lpm4.routes = {}; + +base_ip = 10 * 2^24; + +for i = 1,2^13 do + res = ip(base_ip + (1 * 2^12) * (i - 1)); + + lpm4.routes[i] = { + cidr = {ip = res, depth = 24}, + next_hop_id = (i - 1) % 64, + } +end + +base_ip = 74 * 2^24; + +for i = 1,2^13 do + res = ip(base_ip + (1 * 2^12) * (i - 1)); + + lpm4.routes[2^13 + i] = { + cidr = {ip = res, depth = 24}, + next_hop_id = (i - 1) % 64, + } +end + +return lpm4; diff --git a/VNFs/DPPD-PROX/config/ipv4-4ports.lua b/VNFs/DPPD-PROX/config/ipv4-4ports.lua new file mode 100644 index 00000000..54b102e8 --- /dev/null +++ b/VNFs/DPPD-PROX/config/ipv4-4ports.lua @@ -0,0 +1,98 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local lpm4 = {} +lpm4.next_hops = { + {id = 0, port_id = 0, ip = ip("1.1.1.1"), mac = mac("00:00:00:00:00:01"), mpls = 0x112}, + {id = 1, port_id = 1, ip = ip("2.1.1.1"), mac = mac("00:00:00:00:00:02"), mpls = 0x212}, + {id = 2, port_id = 2, ip = ip("3.1.1.1"), mac = mac("00:00:00:00:00:03"), mpls = 0x312}, + {id = 3, port_id = 3, ip = ip("4.1.1.1"), mac = mac("00:00:00:00:00:04"), mpls = 0x412}, + {id = 4, port_id = 0, ip = ip("5.1.1.1"), mac = mac("00:00:00:00:00:05"), mpls = 0x512}, + {id = 5, port_id = 1, ip = ip("6.1.1.1"), mac = mac("00:00:00:00:00:06"), mpls = 0x612}, + {id = 6, port_id = 2, ip = ip("7.1.1.1"), mac = mac("00:00:00:00:00:07"), mpls = 0x712}, + {id = 7, port_id = 3, ip = ip("8.1.1.1"), mac = mac("00:00:00:00:00:08"), mpls = 0x812}, + {id = 8, port_id = 0, ip = ip("9.1.1.1"), mac = mac("00:00:00:00:00:09"), mpls = 0x912}, + {id = 9, port_id = 1, ip = ip("10.1.1.1"), mac = mac("00:00:00:00:00:10"), mpls = 0x1012}, + {id = 10, port_id = 2, ip = ip("11.1.1.1"), mac = mac("00:00:00:00:00:11"), mpls = 0x1112}, + {id = 11, port_id = 3, ip = ip("12.1.1.1"), mac = mac("00:00:00:00:00:12"), mpls = 0x1212}, + {id = 12, port_id = 0, ip = ip("13.1.1.1"), mac = mac("00:00:00:00:00:13"), mpls = 0x1312}, + {id = 13, port_id = 1, ip = ip("14.1.1.1"), mac = mac("00:00:00:00:00:14"), mpls = 0x1412}, + {id = 14, port_id = 2, ip = ip("15.1.1.1"), mac = mac("00:00:00:00:00:15"), mpls = 0x1512}, + {id = 15, port_id = 3, ip = ip("16.1.1.1"), mac = mac("00:00:00:00:00:16"), mpls = 0x1612}, + {id = 16, port_id = 0, ip = ip("17.1.1.1"), mac = mac("00:00:00:00:00:17"), mpls = 0x1712}, + {id = 17, port_id = 1, ip = ip("18.1.1.1"), mac = mac("00:00:00:00:00:18"), mpls = 0x1812}, + {id = 18, port_id = 2, ip = ip("19.1.1.1"), mac = mac("00:00:00:00:00:19"), mpls = 0x1912}, + {id = 19, port_id = 3, ip = ip("20.1.1.1"), mac = mac("00:00:00:00:00:20"), mpls = 0x2012}, + {id = 20, port_id = 0, ip = ip("21.1.1.1"), mac = mac("00:00:00:00:00:21"), mpls = 0x2112}, + {id = 21, port_id = 1, ip = ip("22.1.1.1"), mac = mac("00:00:00:00:00:22"), mpls = 0x2212}, + {id = 22, port_id = 2, ip = ip("23.1.1.1"), mac = mac("00:00:00:00:00:23"), mpls = 0x2312}, + {id = 23, port_id = 3, ip = ip("24.1.1.1"), mac = mac("00:00:00:00:00:24"), mpls = 0x2412}, + {id = 24, port_id = 0, ip = ip("25.1.1.1"), mac = mac("00:00:00:00:00:25"), mpls = 0x2512}, + {id = 25, port_id = 1, ip = ip("26.1.1.1"), mac = mac("00:00:00:00:00:26"), mpls = 0x2612}, + {id = 26, port_id = 2, ip = ip("27.1.1.1"), mac = mac("00:00:00:00:00:27"), mpls = 0x2712}, + {id = 27, port_id = 3, ip = ip("28.1.1.1"), mac = mac("00:00:00:00:00:28"), mpls = 0x2812}, + {id = 28, port_id = 0, ip = ip("29.1.1.1"), mac = mac("00:00:00:00:00:29"), mpls = 0x2912}, + {id = 29, port_id = 1, ip = ip("30.1.1.1"), mac = mac("00:00:00:00:00:30"), mpls = 0x3012}, + {id = 30, port_id = 2, ip = ip("31.1.1.1"), mac = mac("00:00:00:00:00:31"), mpls = 0x3112}, + {id = 31, port_id = 3, ip = ip("32.1.1.1"), mac = mac("00:00:00:00:00:32"), mpls = 0x3212}, + {id = 32, port_id = 0, ip = ip("33.1.1.1"), mac = mac("00:00:00:00:00:33"), mpls = 0x3312}, + {id = 33, port_id = 1, ip = ip("34.1.1.1"), mac = mac("00:00:00:00:00:34"), mpls = 0x3412}, + {id = 34, port_id = 2, ip = ip("35.1.1.1"), mac = mac("00:00:00:00:00:35"), mpls = 0x3512}, + {id = 35, port_id = 3, ip = ip("36.1.1.1"), mac = mac("00:00:00:00:00:36"), mpls = 0x3612}, + {id = 36, port_id = 0, ip = ip("37.1.1.1"), mac = mac("00:00:00:00:00:37"), mpls = 0x3712}, + {id = 37, port_id = 1, ip = ip("38.1.1.1"), mac = mac("00:00:00:00:00:38"), mpls = 0x3812}, + {id = 38, port_id = 2, ip = ip("39.1.1.1"), mac = mac("00:00:00:00:00:39"), mpls = 0x3912}, + {id = 39, port_id = 3, ip = ip("40.1.1.1"), mac = mac("00:00:00:00:00:40"), mpls = 0x4012}, + {id = 40, port_id = 0, ip = ip("41.1.1.1"), mac = mac("00:00:00:00:00:41"), mpls = 0x4112}, + {id = 41, port_id = 1, ip = ip("42.1.1.1"), mac = mac("00:00:00:00:00:42"), mpls = 0x4212}, + {id = 42, port_id = 2, ip = ip("43.1.1.1"), mac = mac("00:00:00:00:00:43"), mpls = 0x4312}, + {id = 43, port_id = 3, ip = ip("44.1.1.1"), mac = mac("00:00:00:00:00:44"), mpls = 0x4412}, + {id = 44, port_id = 0, ip = ip("45.1.1.1"), mac = mac("00:00:00:00:00:45"), mpls = 0x4512}, + {id = 45, port_id = 1, ip = ip("46.1.1.1"), mac = mac("00:00:00:00:00:46"), mpls = 0x4612}, + {id = 46, port_id = 2, ip = ip("47.1.1.1"), mac = mac("00:00:00:00:00:47"), mpls = 0x4712}, + {id = 47, port_id = 3, ip = ip("48.1.1.1"), mac = mac("00:00:00:00:00:48"), mpls = 0x4812}, + {id = 48, port_id = 0, ip = ip("49.1.1.1"), mac = mac("00:00:00:00:00:49"), mpls = 0x4912}, + {id = 49, port_id = 1, ip = ip("50.1.1.1"), mac = mac("00:00:00:00:00:50"), mpls = 0x5012}, + {id = 50, port_id = 2, ip = ip("51.1.1.1"), mac = mac("00:00:00:00:00:51"), mpls = 0x5112}, + {id = 51, port_id = 3, ip = ip("52.1.1.1"), mac = mac("00:00:00:00:00:52"), mpls = 0x5212}, + {id = 52, port_id = 0, ip = ip("53.1.1.1"), mac = mac("00:00:00:00:00:53"), mpls = 0x5312}, + {id = 53, port_id = 1, ip = ip("54.1.1.1"), mac = mac("00:00:00:00:00:54"), mpls = 0x5412}, + {id = 54, port_id = 2, ip = ip("55.1.1.1"), mac = mac("00:00:00:00:00:55"), mpls = 0x5512}, + {id = 55, port_id = 3, ip = ip("56.1.1.1"), mac = mac("00:00:00:00:00:56"), mpls = 0x5612}, + {id = 56, port_id = 0, ip = ip("57.1.1.1"), mac = mac("00:00:00:00:00:57"), mpls = 0x5712}, + {id = 57, port_id = 1, ip = ip("58.1.1.1"), mac = mac("00:00:00:00:00:58"), mpls = 0x5812}, + {id = 58, port_id = 2, ip = ip("59.1.1.1"), mac = mac("00:00:00:00:00:59"), mpls = 0x5912}, + {id = 59, port_id = 3, ip = ip("60.1.1.1"), mac = mac("00:00:00:00:00:60"), mpls = 0x6012}, + {id = 60, port_id = 0, ip = ip("61.1.1.1"), mac = mac("00:00:00:00:00:61"), mpls = 0x6112}, + {id = 61, port_id = 1, ip = ip("62.1.1.1"), mac = mac("00:00:00:00:00:62"), mpls = 0x6212}, + {id = 62, port_id = 2, ip = ip("63.1.1.1"), mac = mac("00:00:00:00:00:63"), mpls = 0x6312}, + {id = 63, port_id = 3, ip = ip("64.1.1.1"), mac = mac("00:00:00:00:00:64"), mpls = 0x6412}, +} + +lpm4.routes = {}; + +base_ip = 10 * 2^24; + +for i = 1,2^13 do + res = ip(base_ip + (1 *2^12) * (i - 1)); + + lpm4.routes[i] = { + cidr = {ip = res, depth = 24}, + next_hop_id = (i - 1) % 64, + } +end + +return lpm4 diff --git a/VNFs/DPPD-PROX/config/ipv4.lua b/VNFs/DPPD-PROX/config/ipv4.lua new file mode 100644 index 00000000..9fb8c385 --- /dev/null +++ b/VNFs/DPPD-PROX/config/ipv4.lua @@ -0,0 +1,98 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local lpm4 = {} +lpm4.next_hops = { + {id = 0, port_id = 0, ip = ip("1.1.1.1"), mac = mac("00:00:00:00:00:01"), mpls = 0x112}, + {id = 1, port_id = 1, ip = ip("2.1.1.1"), mac = mac("00:00:00:00:00:02"), mpls = 0x212}, + {id = 2, port_id = 0, ip = ip("3.1.1.1"), mac = mac("00:00:00:00:00:03"), mpls = 0x312}, + {id = 3, port_id = 1, ip = ip("4.1.1.1"), mac = mac("00:00:00:00:00:04"), mpls = 0x412}, + {id = 4, port_id = 0, ip = ip("5.1.1.1"), mac = mac("00:00:00:00:00:05"), mpls = 0x512}, + {id = 5, port_id = 1, ip = ip("6.1.1.1"), mac = mac("00:00:00:00:00:06"), mpls = 0x612}, + {id = 6, port_id = 0, ip = ip("7.1.1.1"), mac = mac("00:00:00:00:00:07"), mpls = 0x712}, + {id = 7, port_id = 1, ip = ip("8.1.1.1"), mac = mac("00:00:00:00:00:08"), mpls = 0x812}, + {id = 8, port_id = 0, ip = ip("9.1.1.1"), mac = mac("00:00:00:00:00:09"), mpls = 0x912}, + {id = 9, port_id = 1, ip = ip("10.1.1.1"), mac = mac("00:00:00:00:00:10"), mpls = 0x1012}, + {id = 10, port_id = 0, ip = ip("11.1.1.1"), mac = mac("00:00:00:00:00:11"), mpls = 0x1112}, + {id = 11, port_id = 1, ip = ip("12.1.1.1"), mac = mac("00:00:00:00:00:12"), mpls = 0x1212}, + {id = 12, port_id = 0, ip = ip("13.1.1.1"), mac = mac("00:00:00:00:00:13"), mpls = 0x1312}, + {id = 13, port_id = 1, ip = ip("14.1.1.1"), mac = mac("00:00:00:00:00:14"), mpls = 0x1412}, + {id = 14, port_id = 0, ip = ip("15.1.1.1"), mac = mac("00:00:00:00:00:15"), mpls = 0x1512}, + {id = 15, port_id = 1, ip = ip("16.1.1.1"), mac = mac("00:00:00:00:00:16"), mpls = 0x1612}, + {id = 16, port_id = 0, ip = ip("17.1.1.1"), mac = mac("00:00:00:00:00:17"), mpls = 0x1712}, + {id = 17, port_id = 1, ip = ip("18.1.1.1"), mac = mac("00:00:00:00:00:18"), mpls = 0x1812}, + {id = 18, port_id = 0, ip = ip("19.1.1.1"), mac = mac("00:00:00:00:00:19"), mpls = 0x1912}, + {id = 19, port_id = 1, ip = ip("20.1.1.1"), mac = mac("00:00:00:00:00:20"), mpls = 0x2012}, + {id = 20, port_id = 0, ip = ip("21.1.1.1"), mac = mac("00:00:00:00:00:21"), mpls = 0x2112}, + {id = 21, port_id = 1, ip = ip("22.1.1.1"), mac = mac("00:00:00:00:00:22"), mpls = 0x2212}, + {id = 22, port_id = 0, ip = ip("23.1.1.1"), mac = mac("00:00:00:00:00:23"), mpls = 0x2312}, + {id = 23, port_id = 1, ip = ip("24.1.1.1"), mac = mac("00:00:00:00:00:24"), mpls = 0x2412}, + {id = 24, port_id = 0, ip = ip("25.1.1.1"), mac = mac("00:00:00:00:00:25"), mpls = 0x2512}, + {id = 25, port_id = 1, ip = ip("26.1.1.1"), mac = mac("00:00:00:00:00:26"), mpls = 0x2612}, + {id = 26, port_id = 0, ip = ip("27.1.1.1"), mac = mac("00:00:00:00:00:27"), mpls = 0x2712}, + {id = 27, port_id = 1, ip = ip("28.1.1.1"), mac = mac("00:00:00:00:00:28"), mpls = 0x2812}, + {id = 28, port_id = 0, ip = ip("29.1.1.1"), mac = mac("00:00:00:00:00:29"), mpls = 0x2912}, + {id = 29, port_id = 1, ip = ip("30.1.1.1"), mac = mac("00:00:00:00:00:30"), mpls = 0x3012}, + {id = 30, port_id = 0, ip = ip("31.1.1.1"), mac = mac("00:00:00:00:00:31"), mpls = 0x3112}, + {id = 31, port_id = 1, ip = ip("32.1.1.1"), mac = mac("00:00:00:00:00:32"), mpls = 0x3212}, + {id = 32, port_id = 0, ip = ip("33.1.1.1"), mac = mac("00:00:00:00:00:33"), mpls = 0x3312}, + {id = 33, port_id = 1, ip = ip("34.1.1.1"), mac = mac("00:00:00:00:00:34"), mpls = 0x3412}, + {id = 34, port_id = 0, ip = ip("35.1.1.1"), mac = mac("00:00:00:00:00:35"), mpls = 0x3512}, + {id = 35, port_id = 1, ip = ip("36.1.1.1"), mac = mac("00:00:00:00:00:36"), mpls = 0x3612}, + {id = 36, port_id = 0, ip = ip("37.1.1.1"), mac = mac("00:00:00:00:00:37"), mpls = 0x3712}, + {id = 37, port_id = 1, ip = ip("38.1.1.1"), mac = mac("00:00:00:00:00:38"), mpls = 0x3812}, + {id = 38, port_id = 0, ip = ip("39.1.1.1"), mac = mac("00:00:00:00:00:39"), mpls = 0x3912}, + {id = 39, port_id = 1, ip = ip("40.1.1.1"), mac = mac("00:00:00:00:00:40"), mpls = 0x4012}, + {id = 40, port_id = 0, ip = ip("41.1.1.1"), mac = mac("00:00:00:00:00:41"), mpls = 0x4112}, + {id = 41, port_id = 1, ip = ip("42.1.1.1"), mac = mac("00:00:00:00:00:42"), mpls = 0x4212}, + {id = 42, port_id = 0, ip = ip("43.1.1.1"), mac = mac("00:00:00:00:00:43"), mpls = 0x4312}, + {id = 43, port_id = 1, ip = ip("44.1.1.1"), mac = mac("00:00:00:00:00:44"), mpls = 0x4412}, + {id = 44, port_id = 0, ip = ip("45.1.1.1"), mac = mac("00:00:00:00:00:45"), mpls = 0x4512}, + {id = 45, port_id = 1, ip = ip("46.1.1.1"), mac = mac("00:00:00:00:00:46"), mpls = 0x4612}, + {id = 46, port_id = 0, ip = ip("47.1.1.1"), mac = mac("00:00:00:00:00:47"), mpls = 0x4712}, + {id = 47, port_id = 1, ip = ip("48.1.1.1"), mac = mac("00:00:00:00:00:48"), mpls = 0x4812}, + {id = 48, port_id = 0, ip = ip("49.1.1.1"), mac = mac("00:00:00:00:00:49"), mpls = 0x4912}, + {id = 49, port_id = 1, ip = ip("50.1.1.1"), mac = mac("00:00:00:00:00:50"), mpls = 0x5012}, + {id = 50, port_id = 0, ip = ip("51.1.1.1"), mac = mac("00:00:00:00:00:51"), mpls = 0x5112}, + {id = 51, port_id = 1, ip = ip("52.1.1.1"), mac = mac("00:00:00:00:00:52"), mpls = 0x5212}, + {id = 52, port_id = 0, ip = ip("53.1.1.1"), mac = mac("00:00:00:00:00:53"), mpls = 0x5312}, + {id = 53, port_id = 1, ip = ip("54.1.1.1"), mac = mac("00:00:00:00:00:54"), mpls = 0x5412}, + {id = 54, port_id = 0, ip = ip("55.1.1.1"), mac = mac("00:00:00:00:00:55"), mpls = 0x5512}, + {id = 55, port_id = 1, ip = ip("56.1.1.1"), mac = mac("00:00:00:00:00:56"), mpls = 0x5612}, + {id = 56, port_id = 0, ip = ip("57.1.1.1"), mac = mac("00:00:00:00:00:57"), mpls = 0x5712}, + {id = 57, port_id = 1, ip = ip("58.1.1.1"), mac = mac("00:00:00:00:00:58"), mpls = 0x5812}, + {id = 58, port_id = 0, ip = ip("59.1.1.1"), mac = mac("00:00:00:00:00:59"), mpls = 0x5912}, + {id = 59, port_id = 1, ip = ip("60.1.1.1"), mac = mac("00:00:00:00:00:60"), mpls = 0x6012}, + {id = 60, port_id = 0, ip = ip("61.1.1.1"), mac = mac("00:00:00:00:00:61"), mpls = 0x6112}, + {id = 61, port_id = 1, ip = ip("62.1.1.1"), mac = mac("00:00:00:00:00:62"), mpls = 0x6212}, + {id = 62, port_id = 0, ip = ip("63.1.1.1"), mac = mac("00:00:00:00:00:63"), mpls = 0x6312}, + {id = 63, port_id = 1, ip = ip("64.1.1.1"), mac = mac("00:00:00:00:00:64"), mpls = 0x6412}, +} + +lpm4.routes = {}; + +base_ip = 10 * 2^24; + +for i = 1,2^13 do + res = ip(base_ip + (1 * 2^12) * (i - 1)); + + lpm4.routes[i] = { + cidr = {ip = res, depth = 24}, + next_hop_id = (i - 1) % 64, + } +end + +return lpm4 diff --git a/VNFs/DPPD-PROX/config/ipv4_1port.lua b/VNFs/DPPD-PROX/config/ipv4_1port.lua new file mode 100644 index 00000000..ad5c3472 --- /dev/null +++ b/VNFs/DPPD-PROX/config/ipv4_1port.lua @@ -0,0 +1,98 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local lpm4 = {} +lpm4.next_hops = { + {id = 0, port_id = 0, ip = ip("1.1.1.1"), mac = mac("00:00:00:00:00:01"), mpls = 0x112}, + {id = 1, port_id = 0, ip = ip("2.1.1.1"), mac = mac("00:00:00:00:00:02"), mpls = 0x212}, + {id = 2, port_id = 0, ip = ip("3.1.1.1"), mac = mac("00:00:00:00:00:03"), mpls = 0x312}, + {id = 3, port_id = 0, ip = ip("4.1.1.1"), mac = mac("00:00:00:00:00:04"), mpls = 0x412}, + {id = 4, port_id = 0, ip = ip("5.1.1.1"), mac = mac("00:00:00:00:00:05"), mpls = 0x512}, + {id = 5, port_id = 0, ip = ip("6.1.1.1"), mac = mac("00:00:00:00:00:06"), mpls = 0x612}, + {id = 6, port_id = 0, ip = ip("7.1.1.1"), mac = mac("00:00:00:00:00:07"), mpls = 0x712}, + {id = 7, port_id = 0, ip = ip("8.1.1.1"), mac = mac("00:00:00:00:00:08"), mpls = 0x812}, + {id = 8, port_id = 0, ip = ip("9.1.1.1"), mac = mac("00:00:00:00:00:09"), mpls = 0x912}, + {id = 9, port_id = 0, ip = ip("10.1.1.1"), mac = mac("00:00:00:00:00:10"), mpls = 0x1012}, + {id = 10, port_id = 0, ip = ip("11.1.1.1"), mac = mac("00:00:00:00:00:11"), mpls = 0x1112}, + {id = 11, port_id = 0, ip = ip("12.1.1.1"), mac = mac("00:00:00:00:00:12"), mpls = 0x1212}, + {id = 12, port_id = 0, ip = ip("13.1.1.1"), mac = mac("00:00:00:00:00:13"), mpls = 0x1312}, + {id = 13, port_id = 0, ip = ip("14.1.1.1"), mac = mac("00:00:00:00:00:14"), mpls = 0x1412}, + {id = 14, port_id = 0, ip = ip("15.1.1.1"), mac = mac("00:00:00:00:00:15"), mpls = 0x1512}, + {id = 15, port_id = 0, ip = ip("16.1.1.1"), mac = mac("00:00:00:00:00:16"), mpls = 0x1612}, + {id = 16, port_id = 0, ip = ip("17.1.1.1"), mac = mac("00:00:00:00:00:17"), mpls = 0x1712}, + {id = 17, port_id = 0, ip = ip("18.1.1.1"), mac = mac("00:00:00:00:00:18"), mpls = 0x1812}, + {id = 18, port_id = 0, ip = ip("19.1.1.1"), mac = mac("00:00:00:00:00:19"), mpls = 0x1912}, + {id = 19, port_id = 0, ip = ip("20.1.1.1"), mac = mac("00:00:00:00:00:20"), mpls = 0x2012}, + {id = 20, port_id = 0, ip = ip("21.1.1.1"), mac = mac("00:00:00:00:00:21"), mpls = 0x2112}, + {id = 21, port_id = 0, ip = ip("22.1.1.1"), mac = mac("00:00:00:00:00:22"), mpls = 0x2212}, + {id = 22, port_id = 0, ip = ip("23.1.1.1"), mac = mac("00:00:00:00:00:23"), mpls = 0x2312}, + {id = 23, port_id = 0, ip = ip("24.1.1.1"), mac = mac("00:00:00:00:00:24"), mpls = 0x2412}, + {id = 24, port_id = 0, ip = ip("25.1.1.1"), mac = mac("00:00:00:00:00:25"), mpls = 0x2512}, + {id = 25, port_id = 0, ip = ip("26.1.1.1"), mac = mac("00:00:00:00:00:26"), mpls = 0x2612}, + {id = 26, port_id = 0, ip = ip("27.1.1.1"), mac = mac("00:00:00:00:00:27"), mpls = 0x2712}, + {id = 27, port_id = 0, ip = ip("28.1.1.1"), mac = mac("00:00:00:00:00:28"), mpls = 0x2812}, + {id = 28, port_id = 0, ip = ip("29.1.1.1"), mac = mac("00:00:00:00:00:29"), mpls = 0x2912}, + {id = 29, port_id = 0, ip = ip("30.1.1.1"), mac = mac("00:00:00:00:00:30"), mpls = 0x3012}, + {id = 30, port_id = 0, ip = ip("31.1.1.1"), mac = mac("00:00:00:00:00:31"), mpls = 0x3112}, + {id = 31, port_id = 0, ip = ip("32.1.1.1"), mac = mac("00:00:00:00:00:32"), mpls = 0x3212}, + {id = 32, port_id = 0, ip = ip("33.1.1.1"), mac = mac("00:00:00:00:00:33"), mpls = 0x3312}, + {id = 33, port_id = 0, ip = ip("34.1.1.1"), mac = mac("00:00:00:00:00:34"), mpls = 0x3412}, + {id = 34, port_id = 0, ip = ip("35.1.1.1"), mac = mac("00:00:00:00:00:35"), mpls = 0x3512}, + {id = 35, port_id = 0, ip = ip("36.1.1.1"), mac = mac("00:00:00:00:00:36"), mpls = 0x3612}, + {id = 36, port_id = 0, ip = ip("37.1.1.1"), mac = mac("00:00:00:00:00:37"), mpls = 0x3712}, + {id = 37, port_id = 0, ip = ip("38.1.1.1"), mac = mac("00:00:00:00:00:38"), mpls = 0x3812}, + {id = 38, port_id = 0, ip = ip("39.1.1.1"), mac = mac("00:00:00:00:00:39"), mpls = 0x3912}, + {id = 39, port_id = 0, ip = ip("40.1.1.1"), mac = mac("00:00:00:00:00:40"), mpls = 0x4012}, + {id = 40, port_id = 0, ip = ip("41.1.1.1"), mac = mac("00:00:00:00:00:41"), mpls = 0x4112}, + {id = 41, port_id = 0, ip = ip("42.1.1.1"), mac = mac("00:00:00:00:00:42"), mpls = 0x4212}, + {id = 42, port_id = 0, ip = ip("43.1.1.1"), mac = mac("00:00:00:00:00:43"), mpls = 0x4312}, + {id = 43, port_id = 0, ip = ip("44.1.1.1"), mac = mac("00:00:00:00:00:44"), mpls = 0x4412}, + {id = 44, port_id = 0, ip = ip("45.1.1.1"), mac = mac("00:00:00:00:00:45"), mpls = 0x4512}, + {id = 45, port_id = 0, ip = ip("46.1.1.1"), mac = mac("00:00:00:00:00:46"), mpls = 0x4612}, + {id = 46, port_id = 0, ip = ip("47.1.1.1"), mac = mac("00:00:00:00:00:47"), mpls = 0x4712}, + {id = 47, port_id = 0, ip = ip("48.1.1.1"), mac = mac("00:00:00:00:00:48"), mpls = 0x4812}, + {id = 48, port_id = 0, ip = ip("49.1.1.1"), mac = mac("00:00:00:00:00:49"), mpls = 0x4912}, + {id = 49, port_id = 0, ip = ip("50.1.1.1"), mac = mac("00:00:00:00:00:50"), mpls = 0x5012}, + {id = 50, port_id = 0, ip = ip("51.1.1.1"), mac = mac("00:00:00:00:00:51"), mpls = 0x5112}, + {id = 51, port_id = 0, ip = ip("52.1.1.1"), mac = mac("00:00:00:00:00:52"), mpls = 0x5212}, + {id = 52, port_id = 0, ip = ip("53.1.1.1"), mac = mac("00:00:00:00:00:53"), mpls = 0x5312}, + {id = 53, port_id = 0, ip = ip("54.1.1.1"), mac = mac("00:00:00:00:00:54"), mpls = 0x5412}, + {id = 54, port_id = 0, ip = ip("55.1.1.1"), mac = mac("00:00:00:00:00:55"), mpls = 0x5512}, + {id = 55, port_id = 0, ip = ip("56.1.1.1"), mac = mac("00:00:00:00:00:56"), mpls = 0x5612}, + {id = 56, port_id = 0, ip = ip("57.1.1.1"), mac = mac("00:00:00:00:00:57"), mpls = 0x5712}, + {id = 57, port_id = 0, ip = ip("58.1.1.1"), mac = mac("00:00:00:00:00:58"), mpls = 0x5812}, + {id = 58, port_id = 0, ip = ip("59.1.1.1"), mac = mac("00:00:00:00:00:59"), mpls = 0x5912}, + {id = 59, port_id = 0, ip = ip("60.1.1.1"), mac = mac("00:00:00:00:00:60"), mpls = 0x6012}, + {id = 60, port_id = 0, ip = ip("61.1.1.1"), mac = mac("00:00:00:00:00:61"), mpls = 0x6112}, + {id = 61, port_id = 0, ip = ip("62.1.1.1"), mac = mac("00:00:00:00:00:62"), mpls = 0x6212}, + {id = 62, port_id = 0, ip = ip("63.1.1.1"), mac = mac("00:00:00:00:00:63"), mpls = 0x6312}, + {id = 63, port_id = 0, ip = ip("64.1.1.1"), mac = mac("00:00:00:00:00:64"), mpls = 0x6412}, +} + +lpm4.routes = {}; + +base_ip = 10 * 2^24; + +for i = 1,2^13 do + res = ip(base_ip + (1 * 2^12) * (i - 1)); + + lpm4.routes[i] = { + cidr = {ip = res, depth = 24}, + next_hop_id = (i - 1) % 64, + } +end + +return lpm4 diff --git a/VNFs/DPPD-PROX/config/ipv6.lua b/VNFs/DPPD-PROX/config/ipv6.lua new file mode 100644 index 00000000..dcd6058f --- /dev/null +++ b/VNFs/DPPD-PROX/config/ipv6.lua @@ -0,0 +1,111 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +lpm6 = {} +lpm6.next_hops6 = { + {id = 0, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0000"), mac = mac("fe:80:00:00:00:00"), mpls = 4660}, + {id = 1, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0001"), mac = mac("fe:80:00:00:00:00"), mpls = 4661}, + {id = 2, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0002"), mac = mac("fe:80:00:00:00:00"), mpls = 4662}, + {id = 3, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0003"), mac = mac("fe:80:00:00:00:00"), mpls = 4663}, + {id = 4, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0004"), mac = mac("fe:80:00:00:00:00"), mpls = 4664}, + {id = 5, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0005"), mac = mac("fe:80:00:00:00:00"), mpls = 4665}, + {id = 6, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0006"), mac = mac("fe:80:00:00:00:00"), mpls = 4666}, + {id = 7, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0007"), mac = mac("fe:80:00:00:00:00"), mpls = 4667}, + {id = 8, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0008"), mac = mac("fe:80:00:00:00:00"), mpls = 4668}, + {id = 9, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0009"), mac = mac("fe:80:00:00:00:00"), mpls = 4669}, + {id = 10, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000a"), mac = mac("fe:80:00:00:00:00"), mpls = 4670}, + {id = 11, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000b"), mac = mac("fe:80:00:00:00:00"), mpls = 4671}, + {id = 12, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000c"), mac = mac("fe:80:00:00:00:00"), mpls = 4672}, + {id = 13, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000d"), mac = mac("fe:80:00:00:00:00"), mpls = 4673}, + {id = 14, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000e"), mac = mac("fe:80:00:00:00:00"), mpls = 4674}, + {id = 15, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:000f"), mac = mac("fe:80:00:00:00:00"), mpls = 4675}, + {id = 16, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0010"), mac = mac("fe:80:00:00:00:00"), mpls = 4676}, + {id = 17, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0011"), mac = mac("fe:80:00:00:00:00"), mpls = 4677}, + {id = 18, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0012"), mac = mac("fe:80:00:00:00:00"), mpls = 4678}, + {id = 19, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0013"), mac = mac("fe:80:00:00:00:00"), mpls = 4679}, + {id = 20, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0014"), mac = mac("fe:80:00:00:00:00"), mpls = 4680}, + {id = 21, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0015"), mac = mac("fe:80:00:00:00:00"), mpls = 4681}, + {id = 22, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0016"), mac = mac("fe:80:00:00:00:00"), mpls = 4682}, + {id = 23, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0017"), mac = mac("fe:80:00:00:00:00"), mpls = 4683}, + {id = 24, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0018"), mac = mac("fe:80:00:00:00:00"), mpls = 4684}, + {id = 25, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0019"), mac = mac("fe:80:00:00:00:00"), mpls = 4685}, + {id = 26, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001a"), mac = mac("fe:80:00:00:00:00"), mpls = 4686}, + {id = 27, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001b"), mac = mac("fe:80:00:00:00:00"), mpls = 4687}, + {id = 28, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001c"), mac = mac("fe:80:00:00:00:00"), mpls = 4688}, + {id = 29, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001d"), mac = mac("fe:80:00:00:00:00"), mpls = 4689}, + {id = 30, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001e"), mac = mac("fe:80:00:00:00:00"), mpls = 4690}, + {id = 31, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:001f"), mac = mac("fe:80:00:00:00:00"), mpls = 4691}, + {id = 32, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0020"), mac = mac("fe:80:00:00:00:00"), mpls = 4692}, + {id = 33, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0021"), mac = mac("fe:80:00:00:00:00"), mpls = 4693}, + {id = 34, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0022"), mac = mac("fe:80:00:00:00:00"), mpls = 4694}, + {id = 35, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0023"), mac = mac("fe:80:00:00:00:00"), mpls = 4695}, + {id = 36, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0024"), mac = mac("fe:80:00:00:00:00"), mpls = 4696}, + {id = 37, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0025"), mac = mac("fe:80:00:00:00:00"), mpls = 4697}, + {id = 38, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0026"), mac = mac("fe:80:00:00:00:00"), mpls = 4698}, + {id = 39, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0027"), mac = mac("fe:80:00:00:00:00"), mpls = 4699}, + {id = 40, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0028"), mac = mac("fe:80:00:00:00:00"), mpls = 4700}, + {id = 41, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0029"), mac = mac("fe:80:00:00:00:00"), mpls = 4701}, + {id = 42, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002a"), mac = mac("fe:80:00:00:00:00"), mpls = 4702}, + {id = 43, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002b"), mac = mac("fe:80:00:00:00:00"), mpls = 4703}, + {id = 44, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002c"), mac = mac("fe:80:00:00:00:00"), mpls = 4704}, + {id = 45, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002d"), mac = mac("fe:80:00:00:00:00"), mpls = 4705}, + {id = 46, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002e"), mac = mac("fe:80:00:00:00:00"), mpls = 4706}, + {id = 47, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:002f"), mac = mac("fe:80:00:00:00:00"), mpls = 4707}, + {id = 48, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0030"), mac = mac("fe:80:00:00:00:00"), mpls = 4708}, + {id = 49, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0031"), mac = mac("fe:80:00:00:00:00"), mpls = 4709}, + {id = 50, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0032"), mac = mac("fe:80:00:00:00:00"), mpls = 4710}, + {id = 51, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0033"), mac = mac("fe:80:00:00:00:00"), mpls = 4711}, + {id = 52, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0034"), mac = mac("fe:80:00:00:00:00"), mpls = 4712}, + {id = 53, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0035"), mac = mac("fe:80:00:00:00:00"), mpls = 4713}, + {id = 54, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0036"), mac = mac("fe:80:00:00:00:00"), mpls = 4714}, + {id = 55, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0037"), mac = mac("fe:80:00:00:00:00"), mpls = 4715}, + {id = 56, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0038"), mac = mac("fe:80:00:00:00:00"), mpls = 4716}, + {id = 57, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0039"), mac = mac("fe:80:00:00:00:00"), mpls = 4717}, + {id = 58, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003a"), mac = mac("fe:80:00:00:00:00"), mpls = 4718}, + {id = 59, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003b"), mac = mac("fe:80:00:00:00:00"), mpls = 4719}, + {id = 60, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003c"), mac = mac("fe:80:00:00:00:00"), mpls = 4720}, + {id = 61, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003d"), mac = mac("fe:80:00:00:00:00"), mpls = 4721}, + {id = 62, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003e"), mac = mac("fe:80:00:00:00:00"), mpls = 4722}, + {id = 63, port_id = 0, ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:003f"), mac = mac("fe:80:00:00:00:00"), mpls = 4723}, +} + +lpm6.routes6 = {} + +-- add 1K routes with depth /128 +for i = 1,2^10 do + lpm6.routes6[i] = { + cidr6 = cidr6("fe80:0000:0000:0000:0200:00ff:fe00:".. string.format("%04x", i - 1) .."/128"), + next_hop_id = (i - 1) % 64, + } +end + +-- add 1K routes with depth /64 +for i = 1,2^10 do + lpm6.routes6[i + 2^10] = { + cidr6 = cidr6("fe80:0000:0000:" .. string.format("%04x", i - 1) .. ":0200:00ff:fe00:03e7/64"), + next_hop_id = (i - 1) % 64, + } +end + +-- -- add fallback routes +lpm6.routes6[2^11] = { + cidr6 = cidr6("fe80:0000:0000:03e7:0200:00ff:fe00:03e7/1"), + next_hop_id = 0, +} +lpm6.routes6[2^11 + 1] = { + cidr6 = cidr6("7e80:0000:0000:03e7:0200:00ff:fe00:03e7/1"), + next_hop_id = 0, +} diff --git a/VNFs/DPPD-PROX/config/irq.cfg b/VNFs/DPPD-PROX/config/irq.cfg new file mode 100644 index 00000000..d34c7977 --- /dev/null +++ b/VNFs/DPPD-PROX/config/irq.cfg @@ -0,0 +1,46 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[global] +start time=5 +name=Interrupt (4x) + +[core 0s0] +mode=master + +[core 1s0] +name=irq +task=0 +mode=irq + +[core 2s0] +name=irq +task=0 +mode=irq + +[core 3s0] +name=irq +task=0 +mode=irq + +[core 4s0] +name=irq +task=0 +mode=irq diff --git a/VNFs/DPPD-PROX/config/l2fwd-4ports.cfg b/VNFs/DPPD-PROX/config/l2fwd-4ports.cfg new file mode 100644 index 00000000..27fd08e5 --- /dev/null +++ b/VNFs/DPPD-PROX/config/l2fwd-4ports.cfg @@ -0,0 +1,74 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=50:00:00:00:00:01 +[port 1] +name=if1 +mac=50:00:00:00:00:02 +[port 2] +name=if2 +mac=50:00:00:00:00:03 +[port 3] +name=if3 +mac=50:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=L2 forward (4x) + +[core 0s0] +mode=master + +[core 1s0] +name=L2 fwd +task=0 +mode=l2fwd +rx port=if0 +tx port=if1 +drop=no + +[core 2s0] +name=L2 fwd +task=0 +mode=l2fwd +rx port=if1 +tx port=if0 +drop=no + +[core 3s0] +name=L2 fwd +task=0 +mode=l2fwd +rx port=if2 +tx port=if3 +drop=no + +[core 4s0] +name=L2 fwd +task=0 +mode=l2fwd +rx port=if3 +tx port=if2 +drop=no diff --git a/VNFs/DPPD-PROX/config/l3fwd-4ports.cfg b/VNFs/DPPD-PROX/config/l3fwd-4ports.cfg new file mode 100644 index 00000000..3c452b0e --- /dev/null +++ b/VNFs/DPPD-PROX/config/l3fwd-4ports.cfg @@ -0,0 +1,81 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=50:00:00:00:00:01 +[port 1] +name=if1 +mac=50:00:00:00:00:02 +[port 2] +name=if2 +mac=50:00:00:00:00:03 +[port 3] +name=if3 +mac=50:00:00:00:00:04 + +[defaults] +mempool size=4K + +[lua] +lpm4 = dofile("ipv4.lua") + +[global] +start time=5 +name=Routing (4x) + +[core 0s0] +mode=master + +[core 1s0] +name=Routing +task=0 +mode=routing +route table=lpm4 +rx port=if0 +tx port=if0,if1 +drop=no + +[core 2s0] +name=Routing +task=0 +mode=routing +route table=lpm4 +rx port=if1 +tx port=if0,if1 +drop=no + +[core 3s0] +name=Routing +task=0 +mode=routing +route table=lpm4 +rx port=if2 +tx port=if2,if3 +drop=no + +[core 4s0] +name=Routing +task=0 +mode=routing +route table=lpm4 +rx port=if3 +tx port=if2,if3 +drop=no diff --git a/VNFs/DPPD-PROX/config/lb_5tuple.cfg b/VNFs/DPPD-PROX/config/lb_5tuple.cfg new file mode 100644 index 00000000..e958acca --- /dev/null +++ b/VNFs/DPPD-PROX/config/lb_5tuple.cfg @@ -0,0 +1,52 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[lua] +dofile("tuples.lua") + +[port 0] +name=if0 +mac=hardware +[port 1] +name=if1 +mac=hardware +[port 2] +name=if2 +mac=hardware +[port 3] +name=if3 +mac=hardware + +[defaults] +mempool size=8K + +[global] +start time=5 +name=Load balance 5-tuple + +[core 0s0] +mode=master + +[core 1s0] +name=lb 5tuple +task=0 +mode=lb5tuple +rx port=if0 +tx port=if0,if1,if2,if3 diff --git a/VNFs/DPPD-PROX/config/lw_aftr.cfg b/VNFs/DPPD-PROX/config/lw_aftr.cfg new file mode 100644 index 00000000..eaed2c5d --- /dev/null +++ b/VNFs/DPPD-PROX/config/lw_aftr.cfg @@ -0,0 +1,115 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration creates the functionality of a lwAFTR component of the +; lw4over6 architecture as described in IETF draft available at: +; http://tools.ietf.org/id/draft-ietf-softwire-lw4over6-13.txt +; The lwAFTR simply terminates IPv6 tunnels that carry IPv4 traffic for many +; customers (one tunnel per customer). It consists of two tasks: +; 1) ipv6_encap that encapsulates IPv4 packets into IPv6 and sends those tunnel +; packets towards the customer tunnel endpoint. For this, it must use a +; binding table that associates with each tunnel, a public IPv4 address and a +; set of ports. +; 2) ipv6_decap which handles packets arriving from the tunnel, checks they use +; a source IPv4 address and port combination that matches their originating +; tunnel (based on the same binding table as used by ipv6_encap), removes the +; IPv6 encapsulation and sends them out its "internet" interface. +; The binding table must be loaded in the [lua] section and assigned to the +; tasks using the "tun_bindings" parameter. This configuration loads its binding +; table from the provided ip6_tun_bind.lua but other binding tables can be used. +; +; Binding tables of different sizes and different ranges of addresses and ports +; can be generated by a provided helper script: +; helper-scripts/ipv6_tun/ipv6_tun_bindings.pl -n <num_entries> +; Most other parameters of the generated binding table can be tweaked through +; script command-line switches. For more details, refer to the documentation of +; the script obtained by running it with -help as argument. +; The same script can also generate tables for testing tools to generate packets +; with addresses and ports that match entries from the binding table (randomly +; selecting entries from the binding table). +; Additionally, the helper-scripts/ipv6_tun/gen_4over6.pl script can be used to +; generate pcap files with IPv6 (tunnel) and IPv4 (internet) traffic matching a +; given binding table. +; Example usage: +; ./helper-scripts/ipv6_tun/ipv6_tun_bindings.pl -n 100000 -suffix _100k +; ./helper-scripts/ipv6_tun/gen_4over6.pl -tun -count=200000 \ +; -in ip6_tun_bind_100k.lua -out lwAFTR_tun_100k.pcap +; ./helper-scripts/ipv6_tun/gen_4over6.pl -inet -count=200000 \ +; -in ip6_tun_bind_100k.lua -out lwAFTR_inet_100k.pcap +; The above sequence of invocations generates a binding table with 100k entries, +; written to file ip6_tun_bind_100k.lua (which the PROX configuration file needs +; to load in the [lua] section then assign using the "tun_bindings" parameter), +; and two pcap files to be used to generate traffic that will hit valid entries +; from the binding table. Each pcap file contains 200k packets of either IPv4 or +; IPv6 traffic. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=inet_0 +mac=00:00:00:00:00:01 +[port 1] +name=lwB4_0 +mac=00:00:00:00:00:03 + +[variables] +$tun_hop_limit=5 +$local_ipv6=fe80:0000:0000:0000:0100:00ff:fe00:0000 +$lookup_port_mask=0xffc0 + +[lua] +bindings = dofile("ip6_tun_bind.lua") + +[defaults] +mempool size=16K + +[global] +start time=20 +name=lwAFTR + +[core 0s0] +mode=master + +;***************************************************************************************** +;##### Send Internet IPv4 traffic into IPv6 tunnels, according to binding table #### +[core 1s0] +name=v6_encap +task=0 +mode=ipv6_encap +rx port=inet_0 +tx port=lwB4_0 +local ipv6=$local_ipv6 +tunnel hop limit=$tun_hop_limit +lookup port mask=$lookup_port_mask +tun_bindings=bindings +;***************************************************************************************** +;##### Terminate IPv6 tunnels and transmit IPv4 out to Internet #### +;# Binding table is checked to ensure src IPv4 address and port combo is allocated to the originating tunnel +[core 2s0] +name=v6_decap +task=0 +mode=ipv6_decap +rx port=lwB4_0 +tx port=inet_0 +dst mac=fe:80:00:ee:00:01 +local ipv6=$local_ipv6 +tunnel hop limit=$tun_hop_limit +lookup port mask=$lookup_port_mask +tun_bindings=bindings diff --git a/VNFs/DPPD-PROX/config/nat_table.lua b/VNFs/DPPD-PROX/config/nat_table.lua new file mode 100644 index 00000000..4e3a78ab --- /dev/null +++ b/VNFs/DPPD-PROX/config/nat_table.lua @@ -0,0 +1,26 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +return { + {from = ip("10.10.100.100"), to = ip("192.168.1.1")}, + {from = ip("192.168.1.1"), to = ip("10.10.100.100")}, + {from = ip("192.168.1.101"), to = ip("10.10.10.101")}, + {from = ip("10.10.10.101"), to = ip("192.168.1.101")}, + {from = ip("192.168.1.102"), to = ip("10.10.10.102")}, + {from = ip("10.10.10.102"), to = ip("192.168.1.102")}, + {from = ip("192.168.100.100"), to = ip("10.0.100.100")}, + {from = ip("10.0.100.100"), to = ip("192.168.100.100")}, +} diff --git a/VNFs/DPPD-PROX/config/nop-rings.cfg b/VNFs/DPPD-PROX/config/nop-rings.cfg new file mode 100644 index 00000000..000353ad --- /dev/null +++ b/VNFs/DPPD-PROX/config/nop-rings.cfg @@ -0,0 +1,109 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This configuration is similar to config/nop.cfg with the difference being the +; type of interfaces. The physical ports are replaced by DPDK rings. To use this +; functionality, RTE_TARGET must be set to x86_64-ivshmem-linuxapp-gcc before +; compiling DPDK (i.e. export RTE_TARGET=x86_64-ivshmem-linuxapp-gcc). Also, +; DPDK needs to be compiled with both CONFIG_RTE_BUILD_COMBINE_LIBS=y and +; CONFIG_RTE_LIBRTE_VHOST=y +; The configuration can then be used inside a VM running on top of Open vSwitch. +; The SHA-1 of the Open vSwitch version that has been tested is c78a00b112c9. To +; run the VM, Qemu needs to be patched to support ivshmem with multiple regions +; and the right command line arguments to be used to share memory. Download and +; patch Qemu 1.6.2 using the following commands: +; git clone git://git.qemu-project.org/qemu.git +; cd qemu +; git checkout v1.6.2 +; wget https://01.org/sites/default/files/page/qemu-v1.6.2-ivshmem-dpdk.patch +; patch -p1 < qemu-v1.6.2-ivshmem-dpdk.patch +; ./configure +; make +; After Open vSwitch has been configured with DPDK rings as ports (i.e. ports +; with type dpdkr), Qemu needs to be started with the correct command line +; arguments. Refer to Section 11.1 from the DPDK Programmer's Guide on how to +; build the Qemu command line arguments. +; This configuration uses 4 ports. This means that 8 rings (4 for TX and 4 for +; RX) will need to be shared with the VM through ivshmem. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=00:00:00:00:00:01 +rx_ring=dpdkr0_tx +tx_ring=dpdkr0_rx +[port 1] +name=if1 +mac=00:00:00:00:00:02 +rx_ring=dpdkr1_tx +tx_ring=dpdkr1_rx +[port 2] +name=if2 +mac=00:00:00:00:00:03 +rx_ring=dpdkr2_tx +tx_ring=dpdkr2_rx +[port 3] +name=if3 +mac=00:00:00:00:00:04 +rx_ring=dpdkr3_tx +tx_ring=dpdkr3_rx + +[defaults] +mempool size=4K + +[global] +start time=5 +name=NOP forwarding rings (4x) + +[core 0] +mode=master + +[core 1] +name=nop +task=0 +mode=nop +rx port=if0 +tx port=if1 +drop=no + +[core 2] +name=nop +task=0 +mode=nop +rx port=if1 +tx port=if0 +drop=no + +[core 3] +name=nop +task=0 +mode=nop +rx port=if2 +tx port=if3 +drop=no + +[core 4] +name=nop +task=0 +mode=nop +rx port=if3 +tx port=if2 +drop=no diff --git a/VNFs/DPPD-PROX/config/nop.cfg b/VNFs/DPPD-PROX/config/nop.cfg new file mode 100644 index 00000000..757b1eda --- /dev/null +++ b/VNFs/DPPD-PROX/config/nop.cfg @@ -0,0 +1,86 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +;; +; This is one of the most basic configurations. Note that this configuration +; does not perform any real work as opposed to configurations like BNG/BRAS +; or lwAFTR. This configuration sets up four interfaces and five cores (one +; master core and four worker cores). Packets are passed (i.e. without being +; touched) as follows: +; - interface 0 to interface 1 (handled by core 1) +; - interface 1 to interface 0 (handled by core 2) +; - interface 2 to interface 3 (handled by core 3) +; - interface 3 to interface 2 (handled by core 4) +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=if1 +mac=hardware +[port 2] +name=if2 +mac=hardware +[port 3] +name=if3 +mac=hardware + +[defaults] +mempool size=2K + +[global] +start time=5 +name=NOP forwarding (4x) + +[core 0s0] +mode=master + +[core 1s0] +name=nop +task=0 +mode=nop +rx port=if0 +tx port=if1 +drop=no + +[core 2s0] +name=nop +task=0 +mode=nop +rx port=if1 +tx port=if0 +drop=no + +[core 3s0] +name=nop +task=0 +mode=nop +rx port=if2 +tx port=if3 +drop=no + +[core 4s0] +name=nop +task=0 +mode=nop +rx port=if3 +tx port=if2 +drop=no diff --git a/VNFs/DPPD-PROX/config/nsh_acl.cfg b/VNFs/DPPD-PROX/config/nsh_acl.cfg new file mode 100644 index 00000000..2893bd4d --- /dev/null +++ b/VNFs/DPPD-PROX/config/nsh_acl.cfg @@ -0,0 +1,58 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=if1 +mac=hardware +[lua] +acl_table=dofile("acl_table.lua") + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Firewall + +[core 0s0] +mode=master + +[core 1s0] +name=firewall +task=0 +mode=decapnsh +rx port=if0 +tx cores=${self}t1 + +task=1 +mode=acl +rx ring=yes +tx cores=${self}t2 +rules=acl_table +qinq=no + +task=2 +mode=encapnsh +rx ring=yes +tx port=if0 +drop=no diff --git a/VNFs/DPPD-PROX/config/nsh_nat.cfg b/VNFs/DPPD-PROX/config/nsh_nat.cfg new file mode 100644 index 00000000..bb3bf4bc --- /dev/null +++ b/VNFs/DPPD-PROX/config/nsh_nat.cfg @@ -0,0 +1,57 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=if1 +mac=hardware +[lua] +nat_table = dofile("nat_table.lua") + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Network Address Translation + +[core 0s0] +mode=master + +[core 1s0] +name=nat +task=0 +mode=decapnsh +rx port=if0 +tx cores=${self}t1 + +task=1 +mode=nat +rx ring=yes +tx cores=${self}t2 +nat table=nat_table + +task=2 +mode=encapnsh +rx ring=yes +tx port=if0 +drop=no diff --git a/VNFs/DPPD-PROX/config/pe-4ports.cfg b/VNFs/DPPD-PROX/config/pe-4ports.cfg new file mode 100644 index 00000000..1c7556e1 --- /dev/null +++ b/VNFs/DPPD-PROX/config/pe-4ports.cfg @@ -0,0 +1,170 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=inet0 +mac=hardware +[port 2] +name=if1 +mac=hardware +[port 3] +name=inet1 +mac=hardware +[lua] +lpm4 = dofile("ipv4-2.lua") +dscp_table = dofile("dscp2.lua") +cpe_table = dofile("cpe_table.lua") +acl_table = dofile("rules-2.lua") +user_table = dofile("user_table-pe.lua") +[defaults] +mempool size=65K +[global] +start time=5 +name=PE +cpe table map=if0,if1,if0,if1 + +[variables] +;$wkd=5s1-6s1,5s1h-6s1h; 4 workers +;$wku=7s1-9s1,7s1h-9s1h; 6 workers +$wkd=5s1-6s1,5s1h-6s1h; 6 workers +$wku=7s1-9s1,7s1h-9s1h; 10 workers +[core 0s1] +task=0 +mode=master +tx cores=(${wku})t3m + +[core 1s1] +name=LB-inet0 +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wkd})t0 proto=ipv4 + +[core 1s1h] +name=LB-inet1 +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wkd})t0 proto=ipv4 + +[core 2s1] +name=LB-c0 +task=0 +mode=lbnetwork +rx port=if0 +mempool size=16K +untag mpls=no +tx cores=(${wku})t0 proto=ipv4 + +[core 2s1h] +name=LB-c1 +task=0 +mode=lbnetwork +mempool size=16K +untag mpls=no +rx port=if1 +tx cores=(${wku})t0 proto=ipv4 + +[core $wkd] +name=W-down +task=0 +mode=qinqencapv4 +sub mode=pe +rx ring=yes +tx cores from cpe table=3s1,4s1 remap=if0,if1 +user table=user_table +cpe table=cpe_table +classify=yes +dscp=dscp_table + +[core $wku] +name=W-up +task=0 +mode=acl +rx ring=yes +rules=acl_table +tx cores=${self}t1 +max rules=32768 + +task=1 +mode=police +sub mode=trtcm +police action=yellow io=green,green +police action=drop io=green,yellow +police action=drop io=green,red +police action=drop io=yellow,yellow +police action=drop io=yellow,red +police action=drop io=red,red +cir=4000000000 +pir=4000000000 +cbs=20480 +pbs=20480 +classify=yes +rx ring=yes +tx cores=${self}t2 +users=256 +mark=yes +user table=user_table + +task=2 +mode=untag +ether type=0xa888 +rx ring=yes +tx cores=${self}t3 + +task=3 +mode=routing +add mpls=yes +rx ring=yes +tx ports from routing table=inet0,inet1,inet0,inet1 +route table=lpm4 +mark=yes +mark green=1 +mark yellow=2 +mark red=3 + +[core 3s1] +name=qos1 +task=0 +rx ring=yes +mode=qos +tx port=if0 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table + +[core 4s1] +name=qos1 +rx ring=yes +task=0 +mode=qos +tx port=if1 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table diff --git a/VNFs/DPPD-PROX/config/pe-8ports.cfg b/VNFs/DPPD-PROX/config/pe-8ports.cfg new file mode 100644 index 00000000..b67c48ba --- /dev/null +++ b/VNFs/DPPD-PROX/config/pe-8ports.cfg @@ -0,0 +1,232 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=if0 +mac=hardware +[port 1] +name=inet0 +mac=hardware +[port 2] +name=if1 +mac=hardware +[port 3] +name=inet1 +mac=hardware +[port 4] +name=if2 +mac=hardware +[port 5] +name=inet2 +mac=hardware +[port 6] +name=if3 +mac=hardware +[port 7] +name=inet3 +mac=hardware +[lua] +lpm4 = dofile("ipv4-2.lua") +dscp_table = dofile("dscp2.lua") +cpe_table = dofile("cpe_table.lua") +acl_table = dofile("rules-2.lua") +user_table = dofile("user_table-pe.lua") +[defaults] +mempool size=65K +[global] +start time=5 +name=PE +cpe table map=if0,if1,if2,if3 + +[variables] +$wkd=5s1-6s1,5s1h-6s1h; 4 workers +$wku=7s1-9s1,7s1h-9s1h; 6 workers +;$wkd=5s1-6s1,5s1h-6s1h; 6 workers +;$wku=7s1-9s1,7s1h-9s1h; 10 workers +[core 0s1] +task=0 +mode=master +tx cores=(${wku})t3m + +[core 1s1] +name=LB-inet0 +task=0 +mode=lbnetwork +rx port=inet0 +untag mpls=yes +tx cores=(${wkd})t0 proto=ipv4 + +task=1 +mode=lbnetwork +rx port=inet2 +untag mpls=yes +tx cores=(${wkd})t0 proto=ipv4 + +[core 1s1h] +name=LB-inet1 +task=0 +mode=lbnetwork +untag mpls=yes +rx port=inet1 +tx cores=(${wkd})t0 proto=ipv4 + +task=1 +mode=lbnetwork +untag mpls=yes +rx port=inet3 +tx cores=(${wkd})t0 proto=ipv4 + +[core 2s1] +name=LB-c0 +task=0 +mode=lbnetwork +rx port=if0 +mempool size=16K +untag mpls=no +tx cores=(${wku})t0 proto=ipv4 + +task=1 +mode=lbnetwork +rx port=if2 +mempool size=16K +untag mpls=no +tx cores=(${wku})t0 proto=ipv4 + +[core 2s1h] +name=LB-c1 +task=0 +mode=lbnetwork +mempool size=16K +untag mpls=no +rx port=if1 +tx cores=(${wku})t0 proto=ipv4 + +task=1 +mode=lbnetwork +mempool size=16K +untag mpls=no +rx port=if3 +tx cores=(${wku})t0 proto=ipv4 + +[core $wkd] +name=W-down +task=0 +mode=qinqencapv4 +sub mode=pe +rx ring=yes +tx cores from cpe table=3s1,4s1,3s1h,4s1h remap=if0,if1,if2,if3 +user table=user_table +cpe table=cpe_table +classify=yes +dscp=dscp_table + +[core $wku] +name=W-up +task=0 +mode=acl +rx ring=yes +rules=acl_table +tx cores=${self}t1 +max rules=32768 + +task=1 +mode=police +sub mode=trtcm +police action=yellow io=green,green +police action=drop io=green,yellow +police action=drop io=green,red +police action=drop io=yellow,yellow +police action=drop io=yellow,red +police action=drop io=red,red +cir=4000000000 +pir=4000000000 +cbs=20480 +pbs=20480 +classify=yes +rx ring=yes +tx cores=${self}t2 +users=256 +mark=yes +user table=user_table + +task=2 +mode=untag +ether type=0xa888 +rx ring=yes +tx cores=${self}t3 + +task=3 +mode=routing +add mpls=yes +rx ring=yes +tx ports from routing table=inet0,inet1,inet2,inet3 +route table=lpm4 +mark=yes +mark green=1 +mark yellow=2 +mark red=3 + +[core 3s1] +name=qos1 +task=0 +rx ring=yes +mode=qos +tx port=if0 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table + +[core 4s1] +name=qos1 +rx ring=yes +task=0 +mode=qos +tx port=if1 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table + +[core 3s1h] +name=qos1 +task=0 +rx ring=yes +mode=qos +tx port=if2 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table + +[core 4s1h] +name=qos1 +rx ring=yes +task=0 +mode=qos +tx port=if3 +pipes=256 +pipe tb rate=6250000 +pipe tc rate=6250000 +drop=no +user table=user_table diff --git a/VNFs/DPPD-PROX/config/rules-1.lua b/VNFs/DPPD-PROX/config/rules-1.lua new file mode 100644 index 00000000..20d33f3d --- /dev/null +++ b/VNFs/DPPD-PROX/config/rules-1.lua @@ -0,0 +1,33 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +seven_tuple = function(svlan, cvlan, ip_proto, src, dst, sport, dport, action) + return { + svlan_id = svlan, + cvlan_id = cvlan, + ip_proto = ip_proto, + src_cidr = src, + dst_cidr = dst, + sport = sport, + dport = dport, + action = action, + } +end + +return { + seven_tuple(val_mask(0, 0x0000), val_mask(0, 0x0000), val_mask(17, 0xff), cidr("192.168.0.0/18"), cidr("10.0.0.0/7"), val_range(0,65535), val_range(0,65535), "allow"), + seven_tuple(val_mask(0, 0x0000), val_mask(0, 0x0000), val_mask(17, 0xff), cidr("192.168.0.0/18"), cidr("74.0.0.0/7"), val_range(0,65535), val_range(0,65535), "allow"), +} diff --git a/VNFs/DPPD-PROX/config/rules-2.lua b/VNFs/DPPD-PROX/config/rules-2.lua new file mode 100644 index 00000000..3e2762be --- /dev/null +++ b/VNFs/DPPD-PROX/config/rules-2.lua @@ -0,0 +1,51 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +seven_tuple = function(svlan, cvlan, ip_proto, src, dst, sport, dport, action) + return { + svlan_id = svlan, + cvlan_id = cvlan, + ip_proto = ip_proto, + src_cidr = src, + dst_cidr = dst, + sport = sport, + dport = dport, + action = action, + } +end + +rules2={}; +sports={0,2,4,6,8,10,12,14}; +src_t={{s="192.168.0.0/18", svlan1=0, svlan2=1}, + {s="192.168.16.0/18", svlan1=16, svlan2=17}, + {s="192.168.32.0/18", svlan1=32, svlan2=33}, + {s="192.168.48.0/18", svlan1=48, svlan2=49}, + }; + +for srck,srcv in pairs(src_t) do + for cvlan_mask = 0,255 do + for spk,spv in pairs(sports) do + table.insert(rules2,seven_tuple(val_mask(srcv.svlan1,0x0fff), val_mask(cvlan_mask,0x0fff), val_mask(17,0xff), cidr(srcv.s), cidr("10.0.0.0/7"), val_range(spv,spv), val_range(0,511), "allow")); + table.insert(rules2,seven_tuple(val_mask(srcv.svlan1,0x0fff), val_mask(cvlan_mask,0x0fff), val_mask(17,0xff), cidr(srcv.s), cidr("74.0.0.0/7"), val_range(spv,spv), val_range(0,511), "allow")); + table.insert(rules2,seven_tuple(val_mask(srcv.svlan2,0x0fff), val_mask(cvlan_mask,0x0fff), val_mask(17,0xff), cidr(srcv.s), cidr("10.0.0.0/7"), val_range(spv,spv), val_range(0,511), "allow")); + table.insert(rules2,seven_tuple(val_mask(srcv.svlan2,0x0fff), val_mask(cvlan_mask,0x0fff), val_mask(17,0xff), cidr(srcv.s), cidr("74.0.0.0/7"), val_range(spv,spv), val_range(0,511), "allow")); + table.insert(rules2,rules4); + end + end +end + +return rules2 + diff --git a/VNFs/DPPD-PROX/config/tuples.lua b/VNFs/DPPD-PROX/config/tuples.lua new file mode 100644 index 00000000..268efff4 --- /dev/null +++ b/VNFs/DPPD-PROX/config/tuples.lua @@ -0,0 +1,28 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +tuples = {}; + +for i = 0,2^23-1 do + tuples[i] = {if_out = i%4, + ip_src = i%2^5, + ip_dst = ((i-i%2^5)/2^5)%2^5, + port_src = ((i-i%2^10)/2^10)%2^5, + port_dst = ((i-i%2^15)/2^15)%2^5, + proto = ((i-i%2^20)/2^20)%2^3 * 2^5, + } +end + diff --git a/VNFs/DPPD-PROX/config/user_table-131K-bng.lua b/VNFs/DPPD-PROX/config/user_table-131K-bng.lua new file mode 100644 index 00000000..10475796 --- /dev/null +++ b/VNFs/DPPD-PROX/config/user_table-131K-bng.lua @@ -0,0 +1,74 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- This script generates a user table containing 131072 users. It is +-- meant to be used in a BNG with 4 CPE facing ports. Each of the CPE +-- facing ports has 32768 users behind it. Each user has a unique +-- svlan/cvlan combination. The only difference between the two sets +-- of users is the svlan id. Note that any arbitrary configuration is +-- possible. + +local user_table = {} + +for i = 1,2^15 do + idx = i - 1 + user_table[i] = { + gre_id = idx, + -- svlan_id is 000000000XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +for i = 1,2^15 do + idx = i - 1 + user_table[2^15 + i] = { + gre_id = 2^15 + idx, + -- svlan_id is 000000001XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8 + 0x80, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +for i = 1,2^15 do + idx = i - 1 + user_table[2*2^15 + i] = { + gre_id = 2*2^15 + idx, + -- svlan_id is 000000010XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8 + 0x100, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +for i = 1,2^15 do + idx = i - 1 + user_table[3*2^15 + i] = { + gre_id = 3*2^15 + idx, + -- svlan_id is 000000011XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8 + 0x180, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +return user_table diff --git a/VNFs/DPPD-PROX/config/user_table-65K-bng.lua b/VNFs/DPPD-PROX/config/user_table-65K-bng.lua new file mode 100644 index 00000000..aa62874c --- /dev/null +++ b/VNFs/DPPD-PROX/config/user_table-65K-bng.lua @@ -0,0 +1,50 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- This script generates a user table containing 65536 users. It is +-- meant to be used in a BNG with 2 CPE facing ports. Each of the CPE +-- facing ports has 32768 users behind it. Each user has a unique +-- svlan/cvlan combination. The only difference between the two sets +-- of users is the svlan id. Note that any arbitrary configuration is +-- possible. + +local user_table = {} + +for i = 1,2^15 do + idx = i - 1 + user_table[i] = { + gre_id = idx, + -- svlan_id is 000000000XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +for i = 1,2^15 do + idx = i - 1 + user_table[2^15 + i] = { + gre_id = 2^15 + idx, + -- svlan_id is 000000001XXXXXXX at the bit level + -- cvlan_id is 0000XXXX00XX00XX at the bit level + svlan_id = mask(idx, 0x7f00) / 2^8 + 0x80, + cvlan_id = mask(idx, 0xf0) * 2^4 + mask(idx, 0xc) * 2^2 + mask(idx, 0x3), + user_id = idx, + } +end + +return user_table diff --git a/VNFs/DPPD-PROX/config/user_table-pe.lua b/VNFs/DPPD-PROX/config/user_table-pe.lua new file mode 100644 index 00000000..67afbffa --- /dev/null +++ b/VNFs/DPPD-PROX/config/user_table-pe.lua @@ -0,0 +1,2066 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +return { + {gre_id = 0, svlan_id = 0, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 1, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 0, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 1, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 0, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 1, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 0, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 1, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 0, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 1, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 0, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 1, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 0, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 1, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 0, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 1, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 0, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 1, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 0, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 1, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 0, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 1, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 0, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 1, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 0, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 1, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 0, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 1, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 0, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 1, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 0, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 1, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 0, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 1, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 0, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 1, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 0, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 1, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 0, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 1, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 0, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 1, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 0, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 1, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 0, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 1, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 0, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 1, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 0, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 1, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 0, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 1, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 0, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 1, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 0, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 1, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 0, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 1, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 0, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 1, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 0, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 1, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 0, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 1, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 0, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 1, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 0, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 1, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 0, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 1, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 0, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 1, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 0, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 1, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 0, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 1, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 0, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 1, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 0, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 1, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 0, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 1, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 0, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 1, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 0, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 1, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 0, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 1, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 0, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 1, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 0, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 1, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 0, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 1, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 0, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 1, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 0, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 1, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 0, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 1, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 0, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 1, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 0, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 1, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 0, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 1, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 0, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 1, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 0, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 1, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 0, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 1, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 0, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 1, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 0, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 1, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 0, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 1, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 0, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 1, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 0, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 1, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 0, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 1, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 0, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 1, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 0, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 1, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 0, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 1, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 0, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 1, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 0, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 1, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 0, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 1, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 0, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 1, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 0, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 1, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 0, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 1, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 0, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 1, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 0, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 1, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 0, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 1, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 0, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 1, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 0, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 1, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 0, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 1, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 0, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 1, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 0, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 1, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 0, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 1, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 0, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 1, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 0, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 1, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 0, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 1, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 0, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 1, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 0, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 1, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 0, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 1, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 0, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 1, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 0, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 1, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 0, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 1, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 0, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 1, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 0, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 1, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 0, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 1, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 0, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 1, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 0, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 1, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 0, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 1, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 0, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 1, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 0, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 1, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 0, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 1, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 0, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 1, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 0, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 1, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 0, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 1, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 0, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 1, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 0, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 1, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 0, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 1, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 0, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 1, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 0, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 1, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 0, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 1, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 0, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 1, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 0, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 1, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 0, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 1, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 0, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 1, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 0, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 1, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 0, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 1, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 0, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 1, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 0, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 1, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 0, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 1, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 0, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 1, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 0, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 1, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 0, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 1, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 0, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 1, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 0, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 1, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 0, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 1, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 0, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 1, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 0, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 1, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 0, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 1, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 0, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 1, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 0, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 1, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 0, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 1, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 0, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 1, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 0, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 1, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 0, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 1, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 0, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 1, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 0, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 1, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 0, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 1, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 0, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 1, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 0, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 1, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 0, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 1, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 0, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 1, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 0, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 1, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 0, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 1, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 0, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 1, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 0, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 1, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 0, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 1, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 0, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 1, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 0, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 1, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 0, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 1, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 0, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 1, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 0, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 1, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 0, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 1, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 0, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 1, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 0, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 1, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 0, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 1, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 0, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 1, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 0, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 1, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 0, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 1, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 0, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 1, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 0, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 1, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 0, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 1, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 0, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 1, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 0, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 1, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 0, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 1, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 0, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 1, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 0, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 1, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 0, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 1, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 0, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 1, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 0, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 1, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 0, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 1, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 0, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 1, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 0, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 1, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 0, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 1, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 0, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 1, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 0, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 1, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 0, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 1, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 0, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 1, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 0, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 1, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 0, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 1, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 0, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 1, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 0, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 1, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 0, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 1, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 0, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 1, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 0, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 1, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 0, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 1, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 0, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 1, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 0, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 1, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 0, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 1, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 0, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 1, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 0, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 1, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 0, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 1, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 0, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 1, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 0, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 1, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 0, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 1, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 0, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 1, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 0, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 1, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 0, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 1, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 0, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 1, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 0, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 1, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 0, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 1, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 0, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 1, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 0, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 1, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 0, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 1, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 0, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 1, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 0, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 1, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 0, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 1, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 0, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 1, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 0, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 1, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 0, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 1, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 0, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 1, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 0, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 1, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 0, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 1, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 0, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 1, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 0, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 1, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 0, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 1, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 0, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 1, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 0, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 1, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 0, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 1, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 0, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 1, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 0, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 1, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 0, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 1, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 0, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 1, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 0, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 1, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 0, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 1, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 0, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 1, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 0, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 1, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 0, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 1, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 0, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 1, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 0, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 1, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 0, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 1, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 0, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 1, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 0, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 1, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 0, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 1, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 0, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 1, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 0, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 1, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 0, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 1, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 0, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 1, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 0, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 1, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 0, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 1, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 0, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 1, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 0, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 1, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 0, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 1, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 0, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 1, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 0, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 1, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 0, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 1, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 0, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 1, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 0, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 1, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 0, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 1, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 0, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 1, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 0, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 1, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 0, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 1, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 0, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 1, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 0, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 1, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 0, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 1, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 0, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 1, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 0, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 1, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 0, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 1, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 0, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 1, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 0, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 1, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 16, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 17, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 16, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 17, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 16, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 17, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 16, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 17, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 16, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 17, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 16, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 17, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 16, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 17, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 16, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 17, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 16, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 17, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 16, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 17, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 16, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 17, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 16, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 17, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 16, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 17, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 16, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 17, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 16, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 17, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 16, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 17, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 16, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 17, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 16, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 17, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 16, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 17, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 16, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 17, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 16, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 17, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 16, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 17, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 16, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 17, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 16, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 17, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 16, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 17, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 16, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 17, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 16, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 17, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 16, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 17, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 16, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 17, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 16, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 17, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 16, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 17, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 16, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 17, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 16, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 17, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 16, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 17, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 16, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 17, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 16, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 17, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 16, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 17, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 16, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 17, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 16, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 17, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 16, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 17, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 16, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 17, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 16, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 17, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 16, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 17, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 16, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 17, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 16, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 17, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 16, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 17, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 16, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 17, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 16, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 17, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 16, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 17, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 16, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 17, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 16, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 17, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 16, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 17, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 16, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 17, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 16, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 17, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 16, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 17, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 16, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 17, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 16, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 17, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 16, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 17, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 16, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 17, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 16, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 17, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 16, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 17, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 16, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 17, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 16, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 17, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 16, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 17, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 16, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 17, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 16, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 17, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 16, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 17, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 16, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 17, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 16, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 17, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 16, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 17, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 16, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 17, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 16, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 17, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 16, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 17, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 16, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 17, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 16, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 17, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 16, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 17, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 16, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 17, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 16, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 17, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 16, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 17, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 16, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 17, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 16, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 17, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 16, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 17, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 16, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 17, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 16, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 17, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 16, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 17, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 16, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 17, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 16, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 17, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 16, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 17, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 16, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 17, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 16, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 17, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 16, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 17, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 16, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 17, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 16, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 17, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 16, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 17, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 16, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 17, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 16, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 17, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 16, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 17, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 16, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 17, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 16, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 17, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 16, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 17, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 16, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 17, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 16, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 17, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 16, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 17, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 16, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 17, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 16, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 17, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 16, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 17, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 16, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 17, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 16, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 17, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 16, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 17, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 16, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 17, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 16, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 17, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 16, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 17, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 16, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 17, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 16, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 17, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 16, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 17, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 16, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 17, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 16, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 17, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 16, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 17, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 16, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 17, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 16, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 17, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 16, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 17, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 16, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 17, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 16, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 17, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 16, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 17, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 16, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 17, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 16, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 17, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 16, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 17, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 16, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 17, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 16, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 17, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 16, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 17, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 16, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 17, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 16, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 17, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 16, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 17, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 16, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 17, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 16, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 17, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 16, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 17, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 16, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 17, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 16, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 17, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 16, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 17, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 16, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 17, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 16, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 17, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 16, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 17, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 16, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 17, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 16, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 17, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 16, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 17, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 16, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 17, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 16, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 17, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 16, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 17, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 16, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 17, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 16, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 17, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 16, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 17, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 16, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 17, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 16, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 17, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 16, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 17, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 16, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 17, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 16, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 17, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 16, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 17, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 16, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 17, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 16, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 17, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 16, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 17, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 16, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 17, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 16, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 17, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 16, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 17, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 16, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 17, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 16, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 17, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 16, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 17, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 16, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 17, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 16, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 17, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 16, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 17, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 16, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 17, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 16, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 17, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 16, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 17, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 16, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 17, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 16, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 17, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 16, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 17, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 16, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 17, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 16, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 17, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 16, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 17, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 16, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 17, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 16, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 17, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 16, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 17, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 16, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 17, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 16, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 17, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 16, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 17, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 16, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 17, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 16, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 17, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 16, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 17, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 16, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 17, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 16, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 17, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 16, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 17, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 16, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 17, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 16, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 17, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 16, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 17, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 16, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 17, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 16, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 17, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 16, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 17, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 16, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 17, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 16, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 17, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 16, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 17, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 16, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 17, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 16, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 17, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 16, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 17, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 16, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 17, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 16, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 17, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 16, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 17, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 16, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 17, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 16, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 17, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 16, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 17, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 16, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 17, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 16, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 17, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 16, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 17, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 16, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 17, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 16, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 17, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 16, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 17, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 16, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 17, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 16, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 17, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 16, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 17, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 16, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 17, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 16, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 17, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 16, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 17, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 16, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 17, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 16, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 17, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 16, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 17, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 16, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 17, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 16, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 17, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 16, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 17, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 16, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 17, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 16, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 17, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 16, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 17, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 16, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 17, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 16, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 17, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 16, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 17, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 16, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 17, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 16, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 17, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 16, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 17, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 16, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 17, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 16, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 17, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 16, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 17, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 16, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 17, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 16, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 17, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 16, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 17, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 16, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 17, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 16, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 17, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 16, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 17, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 16, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 17, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 16, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 17, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 16, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 17, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 16, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 17, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 16, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 17, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 16, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 17, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 16, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 17, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 16, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 17, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 16, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 17, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 16, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 17, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 16, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 17, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 16, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 17, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 32, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 33, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 32, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 33, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 32, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 33, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 32, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 33, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 32, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 33, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 32, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 33, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 32, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 33, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 32, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 33, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 32, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 33, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 32, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 33, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 32, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 33, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 32, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 33, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 32, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 33, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 32, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 33, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 32, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 33, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 32, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 33, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 32, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 33, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 32, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 33, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 32, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 33, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 32, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 33, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 32, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 33, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 32, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 33, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 32, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 33, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 32, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 33, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 32, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 33, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 32, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 33, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 32, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 33, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 32, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 33, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 32, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 33, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 32, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 33, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 32, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 33, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 32, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 33, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 32, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 33, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 32, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 33, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 32, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 33, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 32, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 33, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 32, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 33, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 32, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 33, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 32, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 33, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 32, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 33, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 32, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 33, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 32, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 33, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 32, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 33, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 32, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 33, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 32, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 33, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 32, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 33, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 32, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 33, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 32, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 33, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 32, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 33, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 32, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 33, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 32, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 33, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 32, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 33, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 32, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 33, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 32, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 33, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 32, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 33, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 32, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 33, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 32, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 33, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 32, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 33, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 32, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 33, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 32, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 33, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 32, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 33, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 32, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 33, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 32, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 33, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 32, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 33, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 32, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 33, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 32, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 33, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 32, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 33, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 32, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 33, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 32, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 33, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 32, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 33, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 32, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 33, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 32, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 33, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 32, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 33, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 32, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 33, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 32, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 33, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 32, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 33, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 32, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 33, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 32, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 33, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 32, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 33, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 32, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 33, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 32, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 33, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 32, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 33, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 32, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 33, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 32, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 33, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 32, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 33, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 32, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 33, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 32, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 33, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 32, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 33, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 32, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 33, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 32, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 33, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 32, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 33, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 32, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 33, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 32, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 33, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 32, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 33, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 32, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 33, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 32, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 33, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 32, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 33, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 32, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 33, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 32, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 33, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 32, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 33, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 32, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 33, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 32, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 33, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 32, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 33, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 32, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 33, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 32, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 33, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 32, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 33, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 32, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 33, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 32, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 33, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 32, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 33, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 32, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 33, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 32, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 33, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 32, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 33, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 32, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 33, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 32, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 33, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 32, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 33, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 32, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 33, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 32, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 33, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 32, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 33, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 32, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 33, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 32, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 33, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 32, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 33, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 32, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 33, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 32, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 33, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 32, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 33, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 32, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 33, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 32, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 33, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 32, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 33, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 32, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 33, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 32, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 33, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 32, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 33, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 32, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 33, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 32, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 33, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 32, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 33, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 32, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 33, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 32, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 33, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 32, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 33, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 32, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 33, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 32, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 33, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 32, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 33, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 32, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 33, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 32, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 33, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 32, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 33, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 32, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 33, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 32, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 33, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 32, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 33, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 32, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 33, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 32, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 33, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 32, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 33, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 32, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 33, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 32, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 33, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 32, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 33, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 32, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 33, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 32, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 33, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 32, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 33, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 32, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 33, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 32, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 33, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 32, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 33, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 32, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 33, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 32, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 33, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 32, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 33, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 32, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 33, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 32, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 33, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 32, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 33, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 32, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 33, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 32, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 33, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 32, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 33, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 32, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 33, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 32, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 33, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 32, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 33, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 32, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 33, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 32, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 33, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 32, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 33, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 32, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 33, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 32, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 33, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 32, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 33, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 32, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 33, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 32, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 33, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 32, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 33, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 32, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 33, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 32, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 33, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 32, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 33, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 32, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 33, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 32, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 33, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 32, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 33, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 32, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 33, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 32, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 33, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 32, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 33, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 32, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 33, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 32, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 33, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 32, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 33, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 32, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 33, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 32, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 33, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 32, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 33, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 32, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 33, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 32, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 33, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 32, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 33, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 32, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 33, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 32, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 33, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 32, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 33, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 32, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 33, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 32, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 33, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 32, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 33, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 32, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 33, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 32, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 33, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 32, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 33, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 32, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 33, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 32, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 33, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 32, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 33, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 32, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 33, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 32, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 33, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 32, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 33, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 32, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 33, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 32, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 33, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 32, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 33, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 32, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 33, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 32, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 33, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 32, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 33, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 32, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 33, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 32, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 33, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 32, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 33, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 32, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 33, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 32, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 33, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 32, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 33, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 32, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 33, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 32, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 33, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 32, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 33, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 32, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 33, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 32, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 33, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 32, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 33, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 32, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 33, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 32, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 33, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 32, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 33, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 32, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 33, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 32, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 33, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 32, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 33, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 32, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 33, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 32, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 33, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 32, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 33, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 32, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 33, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 32, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 33, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 32, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 33, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 32, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 33, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 32, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 33, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 32, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 33, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 32, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 33, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 32, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 33, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 32, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 33, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 32, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 33, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 32, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 33, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 32, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 33, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 32, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 33, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 32, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 33, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 32, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 33, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 32, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 33, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 32, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 33, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 32, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 33, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 48, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 49, cvlan_id = 0, user_id = 0}, + {gre_id = 0, svlan_id = 48, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 49, cvlan_id = 1, user_id = 1}, + {gre_id = 0, svlan_id = 48, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 49, cvlan_id = 2, user_id = 2}, + {gre_id = 0, svlan_id = 48, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 49, cvlan_id = 3, user_id = 3}, + {gre_id = 0, svlan_id = 48, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 49, cvlan_id = 4, user_id = 4}, + {gre_id = 0, svlan_id = 48, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 49, cvlan_id = 5, user_id = 5}, + {gre_id = 0, svlan_id = 48, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 49, cvlan_id = 6, user_id = 6}, + {gre_id = 0, svlan_id = 48, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 49, cvlan_id = 7, user_id = 7}, + {gre_id = 0, svlan_id = 48, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 49, cvlan_id = 8, user_id = 8}, + {gre_id = 0, svlan_id = 48, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 49, cvlan_id = 9, user_id = 9}, + {gre_id = 0, svlan_id = 48, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 49, cvlan_id = 10, user_id = 10}, + {gre_id = 0, svlan_id = 48, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 49, cvlan_id = 11, user_id = 11}, + {gre_id = 0, svlan_id = 48, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 49, cvlan_id = 12, user_id = 12}, + {gre_id = 0, svlan_id = 48, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 49, cvlan_id = 13, user_id = 13}, + {gre_id = 0, svlan_id = 48, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 49, cvlan_id = 14, user_id = 14}, + {gre_id = 0, svlan_id = 48, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 49, cvlan_id = 15, user_id = 15}, + {gre_id = 0, svlan_id = 48, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 49, cvlan_id = 16, user_id = 16}, + {gre_id = 0, svlan_id = 48, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 49, cvlan_id = 17, user_id = 17}, + {gre_id = 0, svlan_id = 48, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 49, cvlan_id = 18, user_id = 18}, + {gre_id = 0, svlan_id = 48, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 49, cvlan_id = 19, user_id = 19}, + {gre_id = 0, svlan_id = 48, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 49, cvlan_id = 20, user_id = 20}, + {gre_id = 0, svlan_id = 48, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 49, cvlan_id = 21, user_id = 21}, + {gre_id = 0, svlan_id = 48, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 49, cvlan_id = 22, user_id = 22}, + {gre_id = 0, svlan_id = 48, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 49, cvlan_id = 23, user_id = 23}, + {gre_id = 0, svlan_id = 48, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 49, cvlan_id = 24, user_id = 24}, + {gre_id = 0, svlan_id = 48, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 49, cvlan_id = 25, user_id = 25}, + {gre_id = 0, svlan_id = 48, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 49, cvlan_id = 26, user_id = 26}, + {gre_id = 0, svlan_id = 48, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 49, cvlan_id = 27, user_id = 27}, + {gre_id = 0, svlan_id = 48, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 49, cvlan_id = 28, user_id = 28}, + {gre_id = 0, svlan_id = 48, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 49, cvlan_id = 29, user_id = 29}, + {gre_id = 0, svlan_id = 48, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 49, cvlan_id = 30, user_id = 30}, + {gre_id = 0, svlan_id = 48, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 49, cvlan_id = 31, user_id = 31}, + {gre_id = 0, svlan_id = 48, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 49, cvlan_id = 32, user_id = 32}, + {gre_id = 0, svlan_id = 48, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 49, cvlan_id = 33, user_id = 33}, + {gre_id = 0, svlan_id = 48, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 49, cvlan_id = 34, user_id = 34}, + {gre_id = 0, svlan_id = 48, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 49, cvlan_id = 35, user_id = 35}, + {gre_id = 0, svlan_id = 48, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 49, cvlan_id = 36, user_id = 36}, + {gre_id = 0, svlan_id = 48, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 49, cvlan_id = 37, user_id = 37}, + {gre_id = 0, svlan_id = 48, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 49, cvlan_id = 38, user_id = 38}, + {gre_id = 0, svlan_id = 48, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 49, cvlan_id = 39, user_id = 39}, + {gre_id = 0, svlan_id = 48, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 49, cvlan_id = 40, user_id = 40}, + {gre_id = 0, svlan_id = 48, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 49, cvlan_id = 41, user_id = 41}, + {gre_id = 0, svlan_id = 48, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 49, cvlan_id = 42, user_id = 42}, + {gre_id = 0, svlan_id = 48, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 49, cvlan_id = 43, user_id = 43}, + {gre_id = 0, svlan_id = 48, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 49, cvlan_id = 44, user_id = 44}, + {gre_id = 0, svlan_id = 48, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 49, cvlan_id = 45, user_id = 45}, + {gre_id = 0, svlan_id = 48, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 49, cvlan_id = 46, user_id = 46}, + {gre_id = 0, svlan_id = 48, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 49, cvlan_id = 47, user_id = 47}, + {gre_id = 0, svlan_id = 48, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 49, cvlan_id = 48, user_id = 48}, + {gre_id = 0, svlan_id = 48, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 49, cvlan_id = 49, user_id = 49}, + {gre_id = 0, svlan_id = 48, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 49, cvlan_id = 50, user_id = 50}, + {gre_id = 0, svlan_id = 48, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 49, cvlan_id = 51, user_id = 51}, + {gre_id = 0, svlan_id = 48, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 49, cvlan_id = 52, user_id = 52}, + {gre_id = 0, svlan_id = 48, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 49, cvlan_id = 53, user_id = 53}, + {gre_id = 0, svlan_id = 48, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 49, cvlan_id = 54, user_id = 54}, + {gre_id = 0, svlan_id = 48, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 49, cvlan_id = 55, user_id = 55}, + {gre_id = 0, svlan_id = 48, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 49, cvlan_id = 56, user_id = 56}, + {gre_id = 0, svlan_id = 48, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 49, cvlan_id = 57, user_id = 57}, + {gre_id = 0, svlan_id = 48, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 49, cvlan_id = 58, user_id = 58}, + {gre_id = 0, svlan_id = 48, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 49, cvlan_id = 59, user_id = 59}, + {gre_id = 0, svlan_id = 48, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 49, cvlan_id = 60, user_id = 60}, + {gre_id = 0, svlan_id = 48, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 49, cvlan_id = 61, user_id = 61}, + {gre_id = 0, svlan_id = 48, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 49, cvlan_id = 62, user_id = 62}, + {gre_id = 0, svlan_id = 48, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 49, cvlan_id = 63, user_id = 63}, + {gre_id = 0, svlan_id = 48, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 49, cvlan_id = 64, user_id = 64}, + {gre_id = 0, svlan_id = 48, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 49, cvlan_id = 65, user_id = 65}, + {gre_id = 0, svlan_id = 48, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 49, cvlan_id = 66, user_id = 66}, + {gre_id = 0, svlan_id = 48, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 49, cvlan_id = 67, user_id = 67}, + {gre_id = 0, svlan_id = 48, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 49, cvlan_id = 68, user_id = 68}, + {gre_id = 0, svlan_id = 48, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 49, cvlan_id = 69, user_id = 69}, + {gre_id = 0, svlan_id = 48, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 49, cvlan_id = 70, user_id = 70}, + {gre_id = 0, svlan_id = 48, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 49, cvlan_id = 71, user_id = 71}, + {gre_id = 0, svlan_id = 48, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 49, cvlan_id = 72, user_id = 72}, + {gre_id = 0, svlan_id = 48, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 49, cvlan_id = 73, user_id = 73}, + {gre_id = 0, svlan_id = 48, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 49, cvlan_id = 74, user_id = 74}, + {gre_id = 0, svlan_id = 48, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 49, cvlan_id = 75, user_id = 75}, + {gre_id = 0, svlan_id = 48, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 49, cvlan_id = 76, user_id = 76}, + {gre_id = 0, svlan_id = 48, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 49, cvlan_id = 77, user_id = 77}, + {gre_id = 0, svlan_id = 48, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 49, cvlan_id = 78, user_id = 78}, + {gre_id = 0, svlan_id = 48, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 49, cvlan_id = 79, user_id = 79}, + {gre_id = 0, svlan_id = 48, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 49, cvlan_id = 80, user_id = 80}, + {gre_id = 0, svlan_id = 48, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 49, cvlan_id = 81, user_id = 81}, + {gre_id = 0, svlan_id = 48, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 49, cvlan_id = 82, user_id = 82}, + {gre_id = 0, svlan_id = 48, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 49, cvlan_id = 83, user_id = 83}, + {gre_id = 0, svlan_id = 48, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 49, cvlan_id = 84, user_id = 84}, + {gre_id = 0, svlan_id = 48, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 49, cvlan_id = 85, user_id = 85}, + {gre_id = 0, svlan_id = 48, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 49, cvlan_id = 86, user_id = 86}, + {gre_id = 0, svlan_id = 48, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 49, cvlan_id = 87, user_id = 87}, + {gre_id = 0, svlan_id = 48, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 49, cvlan_id = 88, user_id = 88}, + {gre_id = 0, svlan_id = 48, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 49, cvlan_id = 89, user_id = 89}, + {gre_id = 0, svlan_id = 48, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 49, cvlan_id = 90, user_id = 90}, + {gre_id = 0, svlan_id = 48, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 49, cvlan_id = 91, user_id = 91}, + {gre_id = 0, svlan_id = 48, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 49, cvlan_id = 92, user_id = 92}, + {gre_id = 0, svlan_id = 48, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 49, cvlan_id = 93, user_id = 93}, + {gre_id = 0, svlan_id = 48, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 49, cvlan_id = 94, user_id = 94}, + {gre_id = 0, svlan_id = 48, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 49, cvlan_id = 95, user_id = 95}, + {gre_id = 0, svlan_id = 48, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 49, cvlan_id = 96, user_id = 96}, + {gre_id = 0, svlan_id = 48, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 49, cvlan_id = 97, user_id = 97}, + {gre_id = 0, svlan_id = 48, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 49, cvlan_id = 98, user_id = 98}, + {gre_id = 0, svlan_id = 48, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 49, cvlan_id = 99, user_id = 99}, + {gre_id = 0, svlan_id = 48, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 49, cvlan_id = 100, user_id = 100}, + {gre_id = 0, svlan_id = 48, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 49, cvlan_id = 101, user_id = 101}, + {gre_id = 0, svlan_id = 48, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 49, cvlan_id = 102, user_id = 102}, + {gre_id = 0, svlan_id = 48, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 49, cvlan_id = 103, user_id = 103}, + {gre_id = 0, svlan_id = 48, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 49, cvlan_id = 104, user_id = 104}, + {gre_id = 0, svlan_id = 48, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 49, cvlan_id = 105, user_id = 105}, + {gre_id = 0, svlan_id = 48, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 49, cvlan_id = 106, user_id = 106}, + {gre_id = 0, svlan_id = 48, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 49, cvlan_id = 107, user_id = 107}, + {gre_id = 0, svlan_id = 48, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 49, cvlan_id = 108, user_id = 108}, + {gre_id = 0, svlan_id = 48, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 49, cvlan_id = 109, user_id = 109}, + {gre_id = 0, svlan_id = 48, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 49, cvlan_id = 110, user_id = 110}, + {gre_id = 0, svlan_id = 48, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 49, cvlan_id = 111, user_id = 111}, + {gre_id = 0, svlan_id = 48, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 49, cvlan_id = 112, user_id = 112}, + {gre_id = 0, svlan_id = 48, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 49, cvlan_id = 113, user_id = 113}, + {gre_id = 0, svlan_id = 48, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 49, cvlan_id = 114, user_id = 114}, + {gre_id = 0, svlan_id = 48, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 49, cvlan_id = 115, user_id = 115}, + {gre_id = 0, svlan_id = 48, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 49, cvlan_id = 116, user_id = 116}, + {gre_id = 0, svlan_id = 48, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 49, cvlan_id = 117, user_id = 117}, + {gre_id = 0, svlan_id = 48, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 49, cvlan_id = 118, user_id = 118}, + {gre_id = 0, svlan_id = 48, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 49, cvlan_id = 119, user_id = 119}, + {gre_id = 0, svlan_id = 48, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 49, cvlan_id = 120, user_id = 120}, + {gre_id = 0, svlan_id = 48, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 49, cvlan_id = 121, user_id = 121}, + {gre_id = 0, svlan_id = 48, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 49, cvlan_id = 122, user_id = 122}, + {gre_id = 0, svlan_id = 48, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 49, cvlan_id = 123, user_id = 123}, + {gre_id = 0, svlan_id = 48, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 49, cvlan_id = 124, user_id = 124}, + {gre_id = 0, svlan_id = 48, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 49, cvlan_id = 125, user_id = 125}, + {gre_id = 0, svlan_id = 48, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 49, cvlan_id = 126, user_id = 126}, + {gre_id = 0, svlan_id = 48, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 49, cvlan_id = 127, user_id = 127}, + {gre_id = 0, svlan_id = 48, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 49, cvlan_id = 128, user_id = 128}, + {gre_id = 0, svlan_id = 48, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 49, cvlan_id = 129, user_id = 129}, + {gre_id = 0, svlan_id = 48, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 49, cvlan_id = 130, user_id = 130}, + {gre_id = 0, svlan_id = 48, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 49, cvlan_id = 131, user_id = 131}, + {gre_id = 0, svlan_id = 48, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 49, cvlan_id = 132, user_id = 132}, + {gre_id = 0, svlan_id = 48, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 49, cvlan_id = 133, user_id = 133}, + {gre_id = 0, svlan_id = 48, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 49, cvlan_id = 134, user_id = 134}, + {gre_id = 0, svlan_id = 48, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 49, cvlan_id = 135, user_id = 135}, + {gre_id = 0, svlan_id = 48, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 49, cvlan_id = 136, user_id = 136}, + {gre_id = 0, svlan_id = 48, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 49, cvlan_id = 137, user_id = 137}, + {gre_id = 0, svlan_id = 48, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 49, cvlan_id = 138, user_id = 138}, + {gre_id = 0, svlan_id = 48, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 49, cvlan_id = 139, user_id = 139}, + {gre_id = 0, svlan_id = 48, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 49, cvlan_id = 140, user_id = 140}, + {gre_id = 0, svlan_id = 48, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 49, cvlan_id = 141, user_id = 141}, + {gre_id = 0, svlan_id = 48, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 49, cvlan_id = 142, user_id = 142}, + {gre_id = 0, svlan_id = 48, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 49, cvlan_id = 143, user_id = 143}, + {gre_id = 0, svlan_id = 48, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 49, cvlan_id = 144, user_id = 144}, + {gre_id = 0, svlan_id = 48, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 49, cvlan_id = 145, user_id = 145}, + {gre_id = 0, svlan_id = 48, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 49, cvlan_id = 146, user_id = 146}, + {gre_id = 0, svlan_id = 48, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 49, cvlan_id = 147, user_id = 147}, + {gre_id = 0, svlan_id = 48, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 49, cvlan_id = 148, user_id = 148}, + {gre_id = 0, svlan_id = 48, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 49, cvlan_id = 149, user_id = 149}, + {gre_id = 0, svlan_id = 48, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 49, cvlan_id = 150, user_id = 150}, + {gre_id = 0, svlan_id = 48, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 49, cvlan_id = 151, user_id = 151}, + {gre_id = 0, svlan_id = 48, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 49, cvlan_id = 152, user_id = 152}, + {gre_id = 0, svlan_id = 48, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 49, cvlan_id = 153, user_id = 153}, + {gre_id = 0, svlan_id = 48, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 49, cvlan_id = 154, user_id = 154}, + {gre_id = 0, svlan_id = 48, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 49, cvlan_id = 155, user_id = 155}, + {gre_id = 0, svlan_id = 48, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 49, cvlan_id = 156, user_id = 156}, + {gre_id = 0, svlan_id = 48, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 49, cvlan_id = 157, user_id = 157}, + {gre_id = 0, svlan_id = 48, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 49, cvlan_id = 158, user_id = 158}, + {gre_id = 0, svlan_id = 48, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 49, cvlan_id = 159, user_id = 159}, + {gre_id = 0, svlan_id = 48, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 49, cvlan_id = 160, user_id = 160}, + {gre_id = 0, svlan_id = 48, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 49, cvlan_id = 161, user_id = 161}, + {gre_id = 0, svlan_id = 48, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 49, cvlan_id = 162, user_id = 162}, + {gre_id = 0, svlan_id = 48, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 49, cvlan_id = 163, user_id = 163}, + {gre_id = 0, svlan_id = 48, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 49, cvlan_id = 164, user_id = 164}, + {gre_id = 0, svlan_id = 48, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 49, cvlan_id = 165, user_id = 165}, + {gre_id = 0, svlan_id = 48, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 49, cvlan_id = 166, user_id = 166}, + {gre_id = 0, svlan_id = 48, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 49, cvlan_id = 167, user_id = 167}, + {gre_id = 0, svlan_id = 48, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 49, cvlan_id = 168, user_id = 168}, + {gre_id = 0, svlan_id = 48, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 49, cvlan_id = 169, user_id = 169}, + {gre_id = 0, svlan_id = 48, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 49, cvlan_id = 170, user_id = 170}, + {gre_id = 0, svlan_id = 48, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 49, cvlan_id = 171, user_id = 171}, + {gre_id = 0, svlan_id = 48, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 49, cvlan_id = 172, user_id = 172}, + {gre_id = 0, svlan_id = 48, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 49, cvlan_id = 173, user_id = 173}, + {gre_id = 0, svlan_id = 48, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 49, cvlan_id = 174, user_id = 174}, + {gre_id = 0, svlan_id = 48, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 49, cvlan_id = 175, user_id = 175}, + {gre_id = 0, svlan_id = 48, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 49, cvlan_id = 176, user_id = 176}, + {gre_id = 0, svlan_id = 48, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 49, cvlan_id = 177, user_id = 177}, + {gre_id = 0, svlan_id = 48, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 49, cvlan_id = 178, user_id = 178}, + {gre_id = 0, svlan_id = 48, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 49, cvlan_id = 179, user_id = 179}, + {gre_id = 0, svlan_id = 48, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 49, cvlan_id = 180, user_id = 180}, + {gre_id = 0, svlan_id = 48, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 49, cvlan_id = 181, user_id = 181}, + {gre_id = 0, svlan_id = 48, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 49, cvlan_id = 182, user_id = 182}, + {gre_id = 0, svlan_id = 48, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 49, cvlan_id = 183, user_id = 183}, + {gre_id = 0, svlan_id = 48, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 49, cvlan_id = 184, user_id = 184}, + {gre_id = 0, svlan_id = 48, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 49, cvlan_id = 185, user_id = 185}, + {gre_id = 0, svlan_id = 48, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 49, cvlan_id = 186, user_id = 186}, + {gre_id = 0, svlan_id = 48, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 49, cvlan_id = 187, user_id = 187}, + {gre_id = 0, svlan_id = 48, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 49, cvlan_id = 188, user_id = 188}, + {gre_id = 0, svlan_id = 48, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 49, cvlan_id = 189, user_id = 189}, + {gre_id = 0, svlan_id = 48, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 49, cvlan_id = 190, user_id = 190}, + {gre_id = 0, svlan_id = 48, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 49, cvlan_id = 191, user_id = 191}, + {gre_id = 0, svlan_id = 48, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 49, cvlan_id = 192, user_id = 192}, + {gre_id = 0, svlan_id = 48, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 49, cvlan_id = 193, user_id = 193}, + {gre_id = 0, svlan_id = 48, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 49, cvlan_id = 194, user_id = 194}, + {gre_id = 0, svlan_id = 48, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 49, cvlan_id = 195, user_id = 195}, + {gre_id = 0, svlan_id = 48, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 49, cvlan_id = 196, user_id = 196}, + {gre_id = 0, svlan_id = 48, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 49, cvlan_id = 197, user_id = 197}, + {gre_id = 0, svlan_id = 48, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 49, cvlan_id = 198, user_id = 198}, + {gre_id = 0, svlan_id = 48, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 49, cvlan_id = 199, user_id = 199}, + {gre_id = 0, svlan_id = 48, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 49, cvlan_id = 200, user_id = 200}, + {gre_id = 0, svlan_id = 48, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 49, cvlan_id = 201, user_id = 201}, + {gre_id = 0, svlan_id = 48, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 49, cvlan_id = 202, user_id = 202}, + {gre_id = 0, svlan_id = 48, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 49, cvlan_id = 203, user_id = 203}, + {gre_id = 0, svlan_id = 48, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 49, cvlan_id = 204, user_id = 204}, + {gre_id = 0, svlan_id = 48, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 49, cvlan_id = 205, user_id = 205}, + {gre_id = 0, svlan_id = 48, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 49, cvlan_id = 206, user_id = 206}, + {gre_id = 0, svlan_id = 48, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 49, cvlan_id = 207, user_id = 207}, + {gre_id = 0, svlan_id = 48, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 49, cvlan_id = 208, user_id = 208}, + {gre_id = 0, svlan_id = 48, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 49, cvlan_id = 209, user_id = 209}, + {gre_id = 0, svlan_id = 48, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 49, cvlan_id = 210, user_id = 210}, + {gre_id = 0, svlan_id = 48, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 49, cvlan_id = 211, user_id = 211}, + {gre_id = 0, svlan_id = 48, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 49, cvlan_id = 212, user_id = 212}, + {gre_id = 0, svlan_id = 48, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 49, cvlan_id = 213, user_id = 213}, + {gre_id = 0, svlan_id = 48, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 49, cvlan_id = 214, user_id = 214}, + {gre_id = 0, svlan_id = 48, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 49, cvlan_id = 215, user_id = 215}, + {gre_id = 0, svlan_id = 48, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 49, cvlan_id = 216, user_id = 216}, + {gre_id = 0, svlan_id = 48, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 49, cvlan_id = 217, user_id = 217}, + {gre_id = 0, svlan_id = 48, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 49, cvlan_id = 218, user_id = 218}, + {gre_id = 0, svlan_id = 48, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 49, cvlan_id = 219, user_id = 219}, + {gre_id = 0, svlan_id = 48, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 49, cvlan_id = 220, user_id = 220}, + {gre_id = 0, svlan_id = 48, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 49, cvlan_id = 221, user_id = 221}, + {gre_id = 0, svlan_id = 48, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 49, cvlan_id = 222, user_id = 222}, + {gre_id = 0, svlan_id = 48, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 49, cvlan_id = 223, user_id = 223}, + {gre_id = 0, svlan_id = 48, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 49, cvlan_id = 224, user_id = 224}, + {gre_id = 0, svlan_id = 48, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 49, cvlan_id = 225, user_id = 225}, + {gre_id = 0, svlan_id = 48, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 49, cvlan_id = 226, user_id = 226}, + {gre_id = 0, svlan_id = 48, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 49, cvlan_id = 227, user_id = 227}, + {gre_id = 0, svlan_id = 48, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 49, cvlan_id = 228, user_id = 228}, + {gre_id = 0, svlan_id = 48, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 49, cvlan_id = 229, user_id = 229}, + {gre_id = 0, svlan_id = 48, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 49, cvlan_id = 230, user_id = 230}, + {gre_id = 0, svlan_id = 48, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 49, cvlan_id = 231, user_id = 231}, + {gre_id = 0, svlan_id = 48, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 49, cvlan_id = 232, user_id = 232}, + {gre_id = 0, svlan_id = 48, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 49, cvlan_id = 233, user_id = 233}, + {gre_id = 0, svlan_id = 48, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 49, cvlan_id = 234, user_id = 234}, + {gre_id = 0, svlan_id = 48, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 49, cvlan_id = 235, user_id = 235}, + {gre_id = 0, svlan_id = 48, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 49, cvlan_id = 236, user_id = 236}, + {gre_id = 0, svlan_id = 48, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 49, cvlan_id = 237, user_id = 237}, + {gre_id = 0, svlan_id = 48, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 49, cvlan_id = 238, user_id = 238}, + {gre_id = 0, svlan_id = 48, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 49, cvlan_id = 239, user_id = 239}, + {gre_id = 0, svlan_id = 48, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 49, cvlan_id = 240, user_id = 240}, + {gre_id = 0, svlan_id = 48, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 49, cvlan_id = 241, user_id = 241}, + {gre_id = 0, svlan_id = 48, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 49, cvlan_id = 242, user_id = 242}, + {gre_id = 0, svlan_id = 48, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 49, cvlan_id = 243, user_id = 243}, + {gre_id = 0, svlan_id = 48, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 49, cvlan_id = 244, user_id = 244}, + {gre_id = 0, svlan_id = 48, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 49, cvlan_id = 245, user_id = 245}, + {gre_id = 0, svlan_id = 48, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 49, cvlan_id = 246, user_id = 246}, + {gre_id = 0, svlan_id = 48, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 49, cvlan_id = 247, user_id = 247}, + {gre_id = 0, svlan_id = 48, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 49, cvlan_id = 248, user_id = 248}, + {gre_id = 0, svlan_id = 48, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 49, cvlan_id = 249, user_id = 249}, + {gre_id = 0, svlan_id = 48, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 49, cvlan_id = 250, user_id = 250}, + {gre_id = 0, svlan_id = 48, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 49, cvlan_id = 251, user_id = 251}, + {gre_id = 0, svlan_id = 48, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 49, cvlan_id = 252, user_id = 252}, + {gre_id = 0, svlan_id = 48, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 49, cvlan_id = 253, user_id = 253}, + {gre_id = 0, svlan_id = 48, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 49, cvlan_id = 254, user_id = 254}, + {gre_id = 0, svlan_id = 48, cvlan_id = 255, user_id = 255}, + {gre_id = 0, svlan_id = 49, cvlan_id = 255, user_id = 255}, +} diff --git a/VNFs/DPPD-PROX/cqm.c b/VNFs/DPPD-PROX/cqm.c new file mode 100644 index 00000000..19ea19de --- /dev/null +++ b/VNFs/DPPD-PROX/cqm.c @@ -0,0 +1,310 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> + +#include "msr.h" +#include "cqm.h" +#include "log.h" +#include "prox_cfg.h" + +#define IA32_QM_EVTSEL 0xC8D +#define IA32_QM_CTR 0xC8E +#define IA32_QM_ASSOC 0xC8F +#define IA32_QM_L3CA_START 0xC90 +#define IA32_QM_L3CA_END 0xD0F + +#define L3_CACHE_OCCUPANCY 1 +#define L3_TOTAL_EXTERNAL_BANDWIDTH 2 +#define L3_LOCAL_EXTERNAL_BANDWIDTH 3 + +static struct rdt_features rdt_features; +static int cat_features = 0; + +static int stat_core; + +struct reg { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +static void cpuid(struct reg* r, uint32_t a, uint32_t b, uint32_t c, uint32_t d) +{ + asm volatile("cpuid" + : "=a" (r->eax), "=b" (r->ebx), "=c" (r->ecx), "=d" (r->edx) + : "a" (a), "b" (b), "c" (c), "d" (d)); +} + +void read_rdt_info(void) +{ + struct reg r; + int i; + uint64_t tmp_rmid; + int rc; + + cpuid(&r, 0x7, 0x0, 0x0, 0x0); + if ((r.ebx >> 12) & 1) { + plog_info("\tRDT-M. Supports Intel RDT Monitoring capability\n"); + rdt_features.rdtm_supported = 1; + } else { + plog_info("\tDoes not support Intel RDT Monitoring capability\n"); + return; + } + if ((r.ebx >> 15) & 1) { + plog_info("\tRDT-A. Supports Intel RDT Allocation capability\n"); + rdt_features.rdta_supported = 1; + } else { + plog_info("\tDoes not support Intel RDT Allocation capability\n"); + } + + cpuid(&r, 0xf, 0x0, 0x0, 0x0); + if ((r.edx >> 1) & 1) { + plog_info("\tSupports L3 Cache Intel RDT Monitoring\n"); + rdt_features.cmt_supported = 1; + } + plog_info("\tIntel RDT Monitoring has %d maximum RMID\n", r.ebx); + rdt_features.rdtm_max_rmid = r.ebx; + + cpuid(&r, 0xf, 0x0, 0x1, 0x0); + if ((r.edx >> 0) & 1) { + plog_info("\tSupports L3 occupancy monitoring\n"); + rdt_features.cmt_supported = 1; + } + if ((r.edx >> 1) & 1) { + plog_info("\tSupports L3 Total bandwidth monitoring\n"); + rdt_features.mbm_tot_supported = 1; + } + if ((r.edx >> 2) & 1) { + plog_info("\tSupports L3 Local bandwidth monitoring\n"); + rdt_features.mbm_loc_supported = 1; + } + rdt_features.cmt_max_rmid = r.ecx; + rdt_features.upscaling_factor = r.ebx; + rdt_features.event_types = r.edx; + + plog_info("\tL3 Cache Intel RDT Monitoring Capability has %d maximum RMID\n", r.ecx); + plog_info("\tUpscaling_factor = %d\n", rdt_features.upscaling_factor); + + cpuid(&r, 0x10, 0x0, 0x0, 0x0); + if ((r.ebx >> 1) & 1) { + plog_info("\tSupports L3 Cache Allocation Technology\n"); + rdt_features.l3_cat_supported = 1; + } + if ((r.ebx >> 2) & 1) { + plog_info("\tSupports L2 Cache Allocation Technology\n"); + rdt_features.l2_cat_supported = 1; + } + if ((r.ebx >> 3) & 1) { + plog_info("\tSupports MBA Allocation Technology\n"); + rdt_features.mba_supported = 1; + } + + cpuid(&r, 0x10, 0x0, 0x1, 0x0); + if ((r.ecx >> 2) & 1) + plog_info("\tCode and Data Prioritization Technology supported\n"); + plog_info("\tL3 Cache Allocation Technology Enumeration Highest COS number = %d\n", r.edx & 0xffff); + rdt_features.cat_max_rmid = r.edx & 0xffff; + rdt_features.cat_num_ways = r.eax + 1; + + cpuid(&r, 0x10, 0x0, 0x2, 0x0); + plog_info("\tL2 Cache Allocation Technology Enumeration COS number = %d\n", r.edx & 0xffff); + + cpuid(&r, 0x10, 0x0, 0x3, 0x0); + plog_info("\tMemory Bandwidth Allocation Enumeration COS number = %d\n", r.edx & 0xffff); + rdt_features.mba_max_rmid = r.ecx; +} +int mbm_is_supported(void) +{ + return (rdt_features.rdtm_supported && rdt_features.mbm_tot_supported && rdt_features.mbm_loc_supported); +} + +int mba_is_supported(void) +{ + return (rdt_features.rdta_supported && rdt_features.mba_supported); +} + +int cmt_is_supported(void) +{ + if ((rdt_features.rdtm_supported || rdt_features.rdta_supported) && (prox_cfg.flags & DSF_DISABLE_CMT)) { + rdt_features.rdtm_supported = rdt_features.rdta_supported = 0; + plog_info("cqm and cat features disabled by config file\n"); + } + return (rdt_features.rdtm_supported && rdt_features.cmt_supported); +} + +int cat_is_supported(void) +{ + if ((rdt_features.rdtm_supported || rdt_features.rdta_supported) && (prox_cfg.flags & DSF_DISABLE_CMT)) { + rdt_features.rdtm_supported = rdt_features.rdta_supported = 0; + plog_info("cqm and cat features disabled by config file\n"); + } + return (rdt_features.rdta_supported && rdt_features.l3_cat_supported); +} + +int rdt_is_supported(void) +{ + return (cmt_is_supported() || cat_is_supported()); +} + +int rdt_get_features(struct rdt_features* feat) +{ + if (!cmt_is_supported() && !cat_is_supported()) + return 1; + + *feat = rdt_features; + return 0; +} + +int cqm_assoc(uint8_t lcore_id, uint64_t rmid) +{ + uint64_t val = 0; + int ret = 0; + ret = msr_read(&val, lcore_id, IA32_QM_ASSOC); + if (ret != 0) { + plog_err("Unable to read msr %x on core %u\n", IA32_QM_ASSOC, lcore_id); + } + val &= 0x3FFULL; + plog_dbg("core %u, rmid was %lu, now setting to %lu\n", lcore_id, val, rmid); + val |= (uint64_t)(rmid & 0x3FFULL); + ret = msr_write(lcore_id, rmid, IA32_QM_ASSOC); + if (ret != 0) { + plog_err("Unable to set msr %x on core %u to value %lx\n", IA32_QM_ASSOC, lcore_id, val); + } + return ret; +} + +int cqm_assoc_read(uint8_t lcore_id, uint64_t *rmid) +{ + return msr_read(rmid, lcore_id, IA32_QM_ASSOC); +} + +void rdt_init_stat_core(uint8_t lcore_id) +{ + stat_core = lcore_id; +} + +/* read a specific rmid value using core 0 */ +int cmt_read_ctr(uint64_t* ret, uint64_t rmid, uint8_t lcore_id) +{ + uint64_t event_id = L3_CACHE_OCCUPANCY; + + uint64_t es = rmid; + es = (es << 32) | event_id; + + if (msr_write(lcore_id, es, IA32_QM_EVTSEL) < 0) { + return 1; + } + + if (msr_read(ret, lcore_id, IA32_QM_CTR) < 0) { + return 2; + } + + return 0; +} + +int mbm_read_tot_bdw(uint64_t* ret, uint64_t rmid, uint8_t lcore_id) +{ + uint64_t event_id = L3_TOTAL_EXTERNAL_BANDWIDTH; + + uint64_t es = rmid; + es = (es << 32) | event_id; + + if (msr_write(lcore_id, es, IA32_QM_EVTSEL) < 0) { + return 1; + } + + if (msr_read(ret, lcore_id, IA32_QM_CTR) < 0) { + return 2; + } + return 0; +} + +int mbm_read_loc_bdw(uint64_t* ret, uint64_t rmid, uint8_t lcore_id) +{ + uint64_t event_id = L3_LOCAL_EXTERNAL_BANDWIDTH; + + uint64_t es = rmid; + es = (es << 32) | event_id; + + if (msr_write(lcore_id, es, IA32_QM_EVTSEL) < 0) { + return 1; + } + + if (msr_read(ret, lcore_id, IA32_QM_CTR) < 0) { + return 2; + } + return 0; +} + +int cat_log_init(uint8_t lcore_id) +{ + uint64_t tmp_rmid; + int rc, i = 0; + for (i = 0; i < IA32_QM_L3CA_END - IA32_QM_L3CA_START; i++) { + rc = msr_read(&tmp_rmid,lcore_id,IA32_QM_L3CA_START + i); + if (rc < 0) { + break; + } + plog_info("\tAt initialization: Cache allocation set %d (msr %x): mask %lx\n", i, IA32_QM_L3CA_START + i, tmp_rmid); + } + return i; +} + +int cat_set_class_mask(uint8_t lcore_id, uint32_t set, uint32_t mask) +{ + uint64_t tmp_rmid; + int rc; + rc = msr_write(lcore_id, mask, IA32_QM_L3CA_START + set); + if (rc < 0) { + plog_err("Failed to write Cache allocation\n"); + return -1; + } + return 0; +} + +int cat_get_class_mask(uint8_t lcore_id, uint32_t set, uint32_t *mask) +{ + uint64_t tmp_rmid; + int rc; + rc = msr_read(&tmp_rmid,lcore_id,IA32_QM_L3CA_START + set); + if (rc < 0) { + plog_err("Failed to read Cache allocation\n"); + return -1; + } + *mask = tmp_rmid & 0xffffffff; + return 0; +} + +void cat_reset_cache(uint32_t lcore_id) +{ + int rc; + uint32_t mask = (1 << rdt_features.cat_num_ways) -1; + for (uint32_t set = 0; set <= rdt_features.cat_max_rmid; set++) { + rc = msr_write(lcore_id, mask, IA32_QM_L3CA_START + set); + if (rc < 0) { + plog_err("Failed to reset Cache allocation\n"); + } + } +} + +int cat_get_num_ways(void) +{ + return rdt_features.cat_num_ways; +} diff --git a/VNFs/DPPD-PROX/cqm.h b/VNFs/DPPD-PROX/cqm.h new file mode 100644 index 00000000..65b1f453 --- /dev/null +++ b/VNFs/DPPD-PROX/cqm.h @@ -0,0 +1,73 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CQM_H_ +#define _CQM_H_ + +#include <inttypes.h> +#include <stdio.h> + +#define PROX_MAX_CACHE_SET 16 + +struct rdt_features { + uint8_t rdtm_supported; + uint8_t rdta_supported; + uint8_t cmt_supported; + uint8_t mbm_tot_supported; + uint8_t mbm_loc_supported; + uint8_t l3_cat_supported; + uint8_t l2_cat_supported; + uint8_t mba_supported; + uint32_t rdtm_max_rmid; + uint32_t cmt_max_rmid; + uint32_t cat_max_rmid; + uint32_t mba_max_rmid; + uint32_t cat_num_ways; + uint32_t upscaling_factor; + uint32_t event_types; +}; + +struct prox_cache_set_cfg { + uint32_t mask; + uint32_t lcore_id; + int32_t socket_id; +}; + +int rdt_is_supported(void); +int cmt_is_supported(void); +int cat_is_supported(void); +int mbm_is_supported(void); +int mba_is_supported(void); + +int rdt_get_features(struct rdt_features* feat); + +int cqm_assoc(uint8_t lcore_id, uint64_t rmid); +int cqm_assoc_read(uint8_t lcore_id, uint64_t *rmid); + +void rdt_init_stat_core(uint8_t lcore_id); + +int cmt_read_ctr(uint64_t* ret, uint64_t rmid, uint8_t lcore_id); +int mbm_read_tot_bdw(uint64_t* ret, uint64_t rmid, uint8_t lcore_id); +int mbm_read_loc_bdw(uint64_t* ret, uint64_t rmid, uint8_t lcore_id); +void read_rdt_info(void); +extern struct prox_cache_set_cfg prox_cache_set_cfg[PROX_MAX_CACHE_SET]; +int cat_log_init(uint8_t lcore_id); +int cat_set_class_mask(uint8_t lcore_id, uint32_t set, uint32_t mask); +int cat_get_class_mask(uint8_t lcore_id, uint32_t set, uint32_t *mask); +void cat_reset_cache(uint32_t lcore_id); +int cat_get_num_ways(void); + +#endif /* _CQM_H_ */ diff --git a/VNFs/DPPD-PROX/defaults.c b/VNFs/DPPD-PROX/defaults.c new file mode 100644 index 00000000..eeb21b2d --- /dev/null +++ b/VNFs/DPPD-PROX/defaults.c @@ -0,0 +1,186 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <libgen.h> +#include <rte_sched.h> +#include <rte_version.h> + +#include "lconf.h" +#include "defaults.h" +#include "defines.h" +#include "prox_cfg.h" +#include "prox_port_cfg.h" +#include "etypes.h" +#include "toeplitz.h" + +#define TEN_GIGABIT 1250000000 +#define QUEUE_SIZES 128 +#define NB_PIPES 32768 +#define NB_MBUF 4096 +#define RING_RX_SIZE 256 +#define NB_RX_RING_DESC 256 +#define NB_TX_RING_DESC 256 + +/* 1500000 milliseconds */ +#define DEFAULT_CPE_TIMEOUT_MS 1500000 + +/**/ +#if DEFAULT_CPE_TIMEOUT_MS < (DRAIN_TIMEOUT/3000000) +#error DEFAULT_CPE_TIMEOUT_MS too small (needs to be at least 2 ms) +#endif + +static const struct rte_eth_conf default_port_conf = { + .rxmode = { + .split_hdr_size = 0, + .header_split = 0, /* Header Split disabled */ + .hw_ip_checksum = 0, /* IP checksum offload disabled */ + .hw_vlan_filter = 0, /* VLAN filtering disabled */ + .hw_vlan_strip = 0, /* VLAN filtering disabled */ + .jumbo_frame = 0, /* Jumbo frame support disabled */ + .hw_strip_crc = 1, /* CRC stripped by hardware --- always set to 1 in VF */ + .hw_vlan_extend = 0, + .mq_mode = 0 + }, + .rx_adv_conf = { + .rss_conf = { + .rss_key = NULL, + }, + }, + .intr_conf = { + .lsc = 1, /* lsc interrupt feature enabled */ + }, +}; + +static const struct rte_eth_rxconf default_rx_conf = { + .rx_free_thresh = 32, +}; + +static struct rte_eth_txconf default_tx_conf = { + .tx_thresh = { + .pthresh = 32, + .hthresh = 8, + .wthresh = 0, + }, + .tx_free_thresh = 32, /* Use PMD default values */ + .tx_rs_thresh = 32, /* Use PMD default values */ +}; + +static struct rte_sched_port_params port_params_default = { + .name = "port_0", + .socket = 0, + .mtu = 6 + 6 + 4 + 4 + 2 + 1500, + .rate = 0, + .frame_overhead = RTE_SCHED_FRAME_OVERHEAD_DEFAULT, + .n_subports_per_port = 1, + .n_pipes_per_subport = NB_PIPES, + .qsize = {QUEUE_SIZES, QUEUE_SIZES, QUEUE_SIZES, QUEUE_SIZES}, + .pipe_profiles = NULL, + .n_pipe_profiles = 1 /* only one profile */ +}; + +static struct rte_sched_pipe_params pipe_params_default = { + .tb_rate = TEN_GIGABIT / NB_PIPES, + .tb_size = 4000000, + + .tc_rate = {TEN_GIGABIT / NB_PIPES, TEN_GIGABIT / NB_PIPES, TEN_GIGABIT / NB_PIPES, TEN_GIGABIT / NB_PIPES}, + .tc_period = 40, + + .wrr_weights = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, +}; + +static struct rte_sched_subport_params subport_params_default = { + .tb_rate = TEN_GIGABIT, + .tb_size = 4000000, + .tc_rate = {TEN_GIGABIT, TEN_GIGABIT, TEN_GIGABIT, TEN_GIGABIT}, + .tc_period = 40, /* default was 10 */ +}; + +void set_global_defaults(__attribute__((unused)) struct prox_cfg *prox_cfg) +{ +} + +void set_task_defaults(struct prox_cfg* prox_cfg, struct lcore_cfg* lcore_cfg_init) +{ + prox_cfg->master = RTE_MAX_LCORE; + + for (uint32_t i = 0; i < RTE_DIM(prox_cfg->cpe_table_ports); ++i) { + prox_cfg->cpe_table_ports[i] = -1; + } + + for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + struct lcore_cfg *cur_lcore_cfg_init = &lcore_cfg_init[lcore_id]; + cur_lcore_cfg_init->id = lcore_id; + for (uint8_t task_id = 0; task_id < MAX_TASKS_PER_CORE; ++task_id) { + struct task_args *targ = &cur_lcore_cfg_init->targs[task_id]; + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + targ->rx_port_queue[port_id].port = OUT_DISCARD; + } + targ->flags |= TASK_ARG_DROP; + targ->flags |= TASK_ARG_QINQ_ACL; + targ->cpe_table_timeout_ms = DEFAULT_CPE_TIMEOUT_MS; + targ->n_flows = NB_PIPES; + /* configure default values for QoS (can be overwritten by config) */ + targ->qos_conf.port_params = port_params_default; + targ->qos_conf.pipe_params[0] = pipe_params_default; + targ->qos_conf.subport_params[0] = subport_params_default; + targ->qos_conf.port_params.pipe_profiles = targ->qos_conf.pipe_params; + targ->qos_conf.port_params.rate = TEN_GIGABIT; + targ->qinq_tag = ETYPE_8021ad; + targ->n_concur_conn = 8192*2; + + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + targ->tx_port_queue[port_id].port = OUT_DISCARD; + } + + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + targ->mapping[i] = i; // identity + } + + targ->cbs = ETHER_MAX_LEN; + targ->ebs = ETHER_MAX_LEN; + targ->pbs = ETHER_MAX_LEN; + + targ->n_max_rules = 1024; + targ->ring_size = RING_RX_SIZE; + targ->nb_cache_mbuf = MAX_PKT_BURST * 4; + targ->overhead = ETHER_CRC_LEN + 20; + + targ->tunnel_hop_limit = 3; + targ->ctrl_freq = 1000; + targ->lb_friend_core = 0xFF; + targ->mbuf_size = MBUF_SIZE; + targ->n_pkts = 1024*64; + targ->runtime_flags |= TASK_TX_CRC; + targ->accuracy_limit_nsec = 5000; + } + } +} + +void set_port_defaults(void) +{ + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i ) { + prox_port_cfg[i].promiscuous = 1; + prox_port_cfg[i].n_rxd = NB_RX_RING_DESC; + prox_port_cfg[i].n_txd = NB_TX_RING_DESC; + prox_port_cfg[i].port_conf = default_port_conf; + prox_port_cfg[i].tx_conf = default_tx_conf; + prox_port_cfg[i].rx_conf = default_rx_conf; + prox_port_cfg[i].rx_ring[0] = '\0'; + prox_port_cfg[i].tx_ring[0] = '\0'; + prox_port_cfg[i].mtu = PROX_MTU; + } +} diff --git a/VNFs/DPPD-PROX/defaults.h b/VNFs/DPPD-PROX/defaults.h new file mode 100644 index 00000000..5fb31207 --- /dev/null +++ b/VNFs/DPPD-PROX/defaults.h @@ -0,0 +1,46 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _DEFAULTS_H_ +#define _DEFAULTS_H_ + +#include <rte_ether.h> + +struct prox_cfg; +struct lcore_cfg; + +void set_global_defaults(struct prox_cfg* prox_cfg); +void set_task_defaults(struct prox_cfg* prox_cfg, struct lcore_cfg* lcore_cfg_init); +void set_port_defaults(void); + +#define MAX_PKT_BURST 64 +#define MAX_RING_BURST 64 + +#if MAX_RING_BURST < MAX_PKT_BURST +#error MAX_RING_BURST < MAX_PKT_BURST +#endif + +#define NUM_VCPES 65536 +#define GRE_BUCKET_ENTRIES 4 +#define MAX_GRE (NUM_VCPES * GRE_BUCKET_ENTRIES) +#define MAX_RSS_QUEUE_BITS 9 + +#define PROX_VLAN_TAG_SIZE 4 +#define MBUF_SIZE (ETHER_MAX_LEN + (unsigned)sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM + 2 * PROX_VLAN_TAG_SIZE) + +#define PROX_MTU ETHER_MAX_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN + +#endif /* _DEFAULTS_H_ */ diff --git a/VNFs/DPPD-PROX/defines.h b/VNFs/DPPD-PROX/defines.h new file mode 100644 index 00000000..c2309be1 --- /dev/null +++ b/VNFs/DPPD-PROX/defines.h @@ -0,0 +1,59 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _DEFINES_H_ +#define _DEFINES_H_ + +// with 3GHz CPU +#define DRAIN_TIMEOUT __UINT64_C(6000000) // drain TX buffer every 2ms +#define TERM_TIMEOUT __UINT64_C(3000000000) // check if terminated every 1s + +/* DRAIN_TIMEOUT should be smaller than TERM_TIMEOUT as TERM_TIMEOUT + is only checked after DRAIN_TIMEOUT */ +#if TERM_TIMEOUT < DRAIN_TIMEOUT +#error TERM_TIMEOUT < DRAIN_TIMEOUT +#endif + +#ifndef IPv4_BYTES +#define IPv4_BYTES_FMT "%d.%d.%d.%d" +#define IPv4_BYTES(addr) \ + addr[0], addr[1], addr[2], addr[3] +#endif + +#ifndef IPv6_BYTES +#define IPv6_BYTES_FMT "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x" +#define IPv6_BYTES(addr) \ + addr[0], addr[1], addr[2], addr[3], \ + addr[4], addr[5], addr[6], addr[7], \ + addr[8], addr[9], addr[10], addr[11], \ + addr[12], addr[13], addr[14], addr[15] +#endif + +#ifndef MAC_BYTES +#define MAC_BYTES_FMT "%02x:%02x:%02x:%02x:%02x:%02x" + +#define MAC_BYTES(addr) \ + addr[0], addr[1], \ + addr[2], addr[3], \ + addr[4], addr[5] +#endif + +/* assume cpu byte order is little endian */ +#define PKT_TO_LUTQINQ(svlan, cvlan) ((((uint32_t)svlan) & 0x000F) << 4 | (((uint32_t)svlan) & 0xFF00) << 8 | (((uint32_t)cvlan) & 0xFF0F)) + +#define ROUTE_ERR 254 + +#endif /* _DEFINES_H_ */ diff --git a/VNFs/DPPD-PROX/display.c b/VNFs/DPPD-PROX/display.c new file mode 100644 index 00000000..2a351a09 --- /dev/null +++ b/VNFs/DPPD-PROX/display.c @@ -0,0 +1,994 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <curses.h> + +#include <rte_cycles.h> +#include <string.h> +#include <signal.h> +#include <math.h> +#include <signal.h> + +#include "display_latency.h" +#include "display_mempools.h" +#include "display_ports.h" +#include "display_priority.h" +#include "display_rings.h" +#include "display_pkt_len.h" +#include "display_l4gen.h" +#include "display_tasks.h" + +#include "cqm.h" +#include "msr.h" +#include "display.h" +#include "log.h" +#include "commands.h" +#include "main.h" +#include "stats.h" +#include "stats_port.h" +#include "stats_latency.h" +#include "stats_global.h" +#include "stats_core.h" +#include "prox_cfg.h" +#include "prox_assert.h" +#include "version.h" +#include "quit.h" +#include "prox_port_cfg.h" + +static struct screen_state screen_state = { + .pps_unit = 1000, + .chosen_screen = -1, +}; + +static struct display_screen *display_screens[16]; +static struct display_screen *current_screen; +static size_t n_screens; +static size_t longest_title; + +void display_set_pps_unit(int val) +{ + screen_state.pps_unit = val; +} + +/* Set up the display mutex as recursive. This enables threads to use + display_[un]lock() to lock the display when multiple calls to for + instance plog_info() need to be made. */ +static pthread_mutex_t disp_mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + +static void stats_display_layout(uint8_t in_place); + +void display_lock(void) +{ + pthread_mutex_lock(&disp_mtx); +} + +void display_unlock(void) +{ + pthread_mutex_unlock(&disp_mtx); +} + +/* Advanced text output */ +static WINDOW *scr = NULL, *win_txt, *win_general, *win_cmd, *win_stat, *win_title, *win_tabs, *win_help; +static int win_txt_height = 1; +static int title_len; + +static uint16_t max_n_lines; + +static int cmd_cursor_pos; +static const char *cmd_cmd; +static int cmd_len; + +/* Colors used in the interface */ +enum colors { + INVALID_COLOR, + NO_COLOR, + RED_ON_BLACK, + BLACK_ON_CYAN, + BLACK_ON_GREEN, + BLACK_ON_WHITE, + BLACK_ON_YELLOW, + YELLOW_ON_BLACK, + WHITE_ON_RED, + YELLOW_ON_NOTHING, + GREEN_ON_NOTHING, + RED_ON_NOTHING, + BLUE_ON_NOTHING, + CYAN_ON_NOTHING, + MAGENTA_ON_NOTHING, + WHITE_ON_NOTHING, +}; + +int display_getch(void) +{ + int ret; + + display_lock(); + ret = wgetch(scr); + display_unlock(); + + return ret; +} + +void display_cmd(const char *cmd, int cl, int cursor_pos) +{ + cmd_len = cl; + if (cursor_pos == -1 || cursor_pos > cmd_len) + cursor_pos = cmd_len; + cmd_cursor_pos = cursor_pos; + cmd_cmd = cmd; + + display_lock(); + werase(win_cmd); + if (cursor_pos < cmd_len) { + waddnstr(win_cmd, cmd, cursor_pos); + wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK)); + waddnstr(win_cmd, cmd + cursor_pos, 1); + wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW)); + waddnstr(win_cmd, cmd + cursor_pos + 1, cmd_len - (cursor_pos + 1)); + } + else { + waddnstr(win_cmd, cmd, cmd_len); + wmove(win_cmd, cursor_pos, 0); + wbkgdset(win_cmd, COLOR_PAIR(YELLOW_ON_BLACK)); + waddstr(win_cmd, " "); + wbkgdset(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW)); + } + + wattroff(win_stat, A_UNDERLINE); + wrefresh(win_cmd); + display_unlock(); +} + +static void refresh_cmd_win(void) +{ + display_cmd(cmd_cmd, cmd_len, cmd_cursor_pos); +} + +static WINDOW *create_subwindow(int height, int width, int y_pos, int x_pos) +{ + WINDOW *win = subwin(scr, height, width, y_pos, x_pos); + touchwin(scr); + return win; +} + +/* The limit parameter sets the last column that something can be + printed. If characters would be printed _past_ the limit, the last + character printed within the limit will be a '~' to signify that + the string cut off. The limit parameter will be ignored if its + value is -1 */ +static inline int mvwaddstrv(WINDOW *win, int y, int x, int limit, const char *fmt, va_list ap) +{ + char buf[1024]; + int ret; + + ret = vsnprintf(buf, sizeof(buf), fmt, ap); + int len = ret; + + wmove(win, y, x); + if (x > COLS - 1) { + return 0; + } + + /* To prevent strings from wrapping, cut the string at the end + of the screen. */ + if (x + len > COLS) { + buf[COLS - 1 - x] = 0; + len = COLS - x; + } + + if (limit != -1 && x + len > limit) { + int new_len = limit - x; + + if (new_len < 0) + return 0; + buf[new_len] = '~'; + buf[new_len + 1] = 0; + } + + waddstr(win, buf); + return ret; +} + +/* Format string capable [mv]waddstr() wrappers */ +__attribute__((format(printf, 4, 5))) static inline int mvwaddstrf(WINDOW* win, int y, int x, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = mvwaddstrv(win, y, x, -1, fmt, ap); + va_end(ap); + return ret; +} + +__attribute__((format(printf, 5, 6))) static inline int mvwaddstrf_limit(WINDOW* win, int y, int x, int limit, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = mvwaddstrv(win, y, x, limit, fmt, ap); + va_end(ap); + return ret; +} + +// red: link down; Green: link up +static short link_color(const uint8_t if_port) +{ + return COLOR_PAIR(prox_port_cfg[if_port].link_up? GREEN_ON_NOTHING : RED_ON_NOTHING); +} + +static void (*ncurses_sigwinch)(int); + +static void sigwinch(int in) +{ + if (ncurses_sigwinch) + ncurses_sigwinch(in); + refresh(); + stats_display_layout(0); +} + +static void set_signal_handler(void) +{ + struct sigaction old; + + sigaction(SIGWINCH, NULL, &old); + ncurses_sigwinch = old.sa_handler; + + signal(SIGWINCH, sigwinch); +} + +void display_column_port_ring(const struct display_column *display_column, int row, struct port_queue *ports, int port_count, struct rte_ring **rings, int ring_count) +{ + if (row >= max_n_lines) + return; + + int pos = display_column->offset; + int limit = pos + display_column->width; + + for (int i = 0; i < port_count && pos < limit; i++) { + wbkgdset(win_stat, link_color(ports[i].port)); + pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%u", ports[i].port); + wbkgdset(win_stat, COLOR_PAIR(NO_COLOR)); + + if (i != port_count - 1) + pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, " "); + } + + for (uint8_t ring_id = 0; ring_id < ring_count && pos < limit; ++ring_id) { + pos += mvwaddstrf_limit(win_stat, row + 2, pos, limit, "%s", rings[ring_id]->name); + } +} + +static void display_add_screen(struct display_screen *screen) +{ + display_screens[n_screens++] = screen; + if (longest_title < strlen(screen->title)) + longest_title = strlen(screen->title); +} + +static void display_init_screens(void) +{ + if (n_screens) + return; + + display_add_screen(display_tasks()); + display_add_screen(display_ports()); + display_add_screen(display_mempools()); + display_add_screen(display_latency()); + display_add_screen(display_rings()); + display_add_screen(display_l4gen()); + display_add_screen(display_pkt_len()); + display_add_screen(display_priority()); +} + +void display_init(void) +{ + scr = initscr(); + start_color(); + /* Assign default foreground/background colors to color number -1 */ + use_default_colors(); + + init_pair(NO_COLOR, -1, -1); + init_pair(RED_ON_BLACK, COLOR_RED, COLOR_BLACK); + init_pair(BLACK_ON_CYAN, COLOR_BLACK, COLOR_CYAN); + init_pair(BLACK_ON_GREEN, COLOR_BLACK, COLOR_GREEN); + init_pair(BLACK_ON_WHITE, COLOR_BLACK, COLOR_WHITE); + init_pair(BLACK_ON_YELLOW, COLOR_BLACK, COLOR_YELLOW); + init_pair(YELLOW_ON_BLACK, COLOR_YELLOW, COLOR_BLACK); + init_pair(WHITE_ON_RED, COLOR_WHITE, COLOR_RED); + init_pair(YELLOW_ON_NOTHING, COLOR_YELLOW, -1); + init_pair(GREEN_ON_NOTHING, COLOR_GREEN, -1); + init_pair(RED_ON_NOTHING, COLOR_RED, -1); + init_pair(BLUE_ON_NOTHING, COLOR_BLUE, -1); + init_pair(CYAN_ON_NOTHING, COLOR_CYAN, -1); + init_pair(MAGENTA_ON_NOTHING, COLOR_MAGENTA, -1); + init_pair(WHITE_ON_NOTHING, COLOR_WHITE, -1); + /* nodelay(scr, TRUE); */ + noecho(); + curs_set(0); + /* Create fullscreen log window. When stats are displayed + later, it is recreated with appropriate dimensions. */ + win_txt = create_subwindow(0, 0, 0, 0); + wbkgd(win_txt, COLOR_PAIR(0)); + + idlok(win_txt, FALSE); + /* Get scrolling */ + scrollok(win_txt, TRUE); + /* Leave cursor where it was */ + leaveok(win_txt, TRUE); + + refresh(); + + set_signal_handler(); + + max_n_lines = (LINES - 5 - 2 - 3); + /* core_port_height = max_n_lines < stats_get_n_tasks_tot()? max_n_lines : stats_get_n_tasks_tot(); */ + + display_init_screens(); + display_screen(0); + stats_display_layout(0); +} + +static void display_page_recalc_offsets(struct display_page *display_page) +{ + struct display_table *table; + struct display_column *col; + int total_offset = 0; + + for (int i = 0; i < display_page->n_tables; ++i) { + table = &display_page->tables[i]; + + if (i != 0) + total_offset += 1; + table->offset = total_offset; + for (int j = 0; j < table->n_cols; ++j) { + col = &table->cols[j]; + col->offset = total_offset; + if (j + 1 != table->n_cols) + total_offset += 1; + total_offset += col->width; + } + table->width = total_offset - table->offset; + } +} + +void display_page_init(struct display_page *display_page) +{ + struct display_table *table; + struct display_column *col; + int table_width = 0; + int table_offset = 0; + + memset(display_page, 0, sizeof(*display_page)); + display_page->n_tables = 0; + for (size_t i = 0; i < sizeof(display_page->tables)/sizeof(display_page->tables[0]); ++i) { + table = &display_page->tables[i]; + for (size_t j = 0; j < sizeof(table->cols)/sizeof(table->cols[0]); ++j) { + col = &table->cols[j]; + col->display_page = display_page; + } + } +} + +struct display_table *display_page_add_table(struct display_page *display_page) +{ + struct display_table *table = &display_page->tables[display_page->n_tables]; + + display_page->n_tables++; + return table; +} + +void display_table_init(struct display_table *table, const char *title) +{ + strcpy(table->title, title); + table->n_cols = 0; +} + +struct display_column *display_table_add_col(struct display_table *table) +{ + struct display_column *col = &table->cols[table->n_cols]; + + table->n_cols++; + return col; +} + +void display_column_init(struct display_column *display_column, const char *title, unsigned width) +{ + if (width < strlen(title)) + width = strlen(title); + + strcpy(display_column->title, title); + display_column->width = width; + display_page_recalc_offsets(display_column->display_page); +} + +int display_column_get_width(const struct display_column *display_column) +{ + return display_column->width; +} + +void display_page_draw_frame(const struct display_page *display_page, int height) +{ + const struct display_table *table; + const struct display_column *col; + + wattron(win_stat, A_BOLD); + wbkgdset(win_stat, COLOR_PAIR(YELLOW_ON_NOTHING)); + + for (int i = 0; i < display_page->n_tables; ++i) { + table = &display_page->tables[i]; + + if (i != 0) + mvwvline(win_stat, 0, table->offset - 1, ACS_VLINE, height + 2); + + mvwaddstrf(win_stat, 0, table->offset + table->width / 2 - strlen(table->title) / 2, "%s", table->title); + for (int j = 0; j < table->n_cols; ++j) { + col = &table->cols[j]; + + if (j != 0) + mvwvline(win_stat, 1, col->offset - 1, ACS_VLINE, height + 1); + mvwaddstrf(win_stat, 1, col->offset + col->width / 2 - strlen(col->title) / 2, "%s", col->title); + } + + if (i + 1 == display_page->n_tables) + mvwvline(win_stat, 0, table->offset + table->width, ACS_VLINE, height + 2); + } + wbkgdset(win_stat, COLOR_PAIR(NO_COLOR)); + wattroff(win_stat, A_BOLD); +} + +void display_column_print(const struct display_column *display_column, int row, const char *fmt, ...) +{ + if (row >= max_n_lines) + return; + + va_list ap; + char buffer[128] = {0}; + char *to_print = buffer + 64; + + va_start(ap, fmt); + int len = vsnprintf(to_print, sizeof(buffer) - 64, fmt, ap); + va_end(ap); + + int offset = 0; + /* If column is too long, add ~ at the end. If it is too + short, align on the right. */ + if (len > display_column->width) { + to_print[display_column->width - 1] = '~'; + to_print[display_column->width] = '\0'; + } else { + int diff = display_column->width - len; + + to_print += len; + to_print -= display_column->width; + for (int i = 0; i < diff; i++) + to_print[i] = ' '; + } + + mvwaddstrf(win_stat, row + 2, display_column->offset, "%s", to_print); +} + +void display_column_print_core_task(const struct display_column *display_column, int row, struct lcore_cfg *lconf, struct task_args *targ) +{ + if (row >= max_n_lines) + return; + + if (lconf->n_tasks_run == 0) { + wattron(win_stat, A_BOLD); + wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING)); + } + if (targ->id == 0) + mvwaddstrf(win_stat, row + 2, display_column->offset, "%2u/", lconf->id); + if (lconf->n_tasks_run == 0) { + wattroff(win_stat, A_BOLD); + wbkgdset(win_stat, COLOR_PAIR(NO_COLOR)); + } + if (!lconf_task_is_running(lconf, targ->id)) { + wattron(win_stat, A_BOLD); + wbkgdset(win_stat, COLOR_PAIR(RED_ON_NOTHING)); + } + mvwaddstrf(win_stat, row + 2, display_column->offset + 3, "%1u", targ->id); + if (!lconf_task_is_running(lconf, targ->id)) { + wattroff(win_stat, A_BOLD); + wbkgdset(win_stat, COLOR_PAIR(NO_COLOR)); + } +} + +static void redraw_tabs(unsigned screen_id) +{ + const size_t len = longest_title + 1; + + for (size_t i = 0; i < n_screens; ++i) { + if (i == screen_id) + wbkgdset(win_tabs, COLOR_PAIR(BLACK_ON_GREEN)); + + mvwaddstrf(win_tabs, 0, i*(len + 3), "%zu ", i+1); + if (i != screen_id) + wbkgdset(win_tabs, COLOR_PAIR(GREEN_ON_NOTHING)); + mvwaddstrf(win_tabs, 0, i*(len + 3) + 2, "%s", display_screens[i]->title); + for (size_t j = strlen(display_screens[i]->title); j < len - 1; ++j) + mvwaddstrf(win_tabs, 0, i*(len + 3) + 2 + j, " "); + if (i != screen_id) + wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR)); + if (i == screen_id) + wbkgdset(win_tabs, COLOR_PAIR(NO_COLOR)); + } + + wrefresh(win_tabs); +} + +static void draw_title(void) +{ + char title_str[128]; + + snprintf(title_str, sizeof(title_str), "%s %s: %s", PROGRAM_NAME, VERSION_STR, prox_cfg.name); + + wbkgd(win_title, COLOR_PAIR(BLACK_ON_GREEN)); + title_len = strlen(title_str); + mvwaddstrf(win_title, 0, (COLS - title_len)/2, "%s", title_str); + + redraw_tabs(screen_state.chosen_screen); +} + +static void draw_general_frame(void) +{ + if (screen_state.toggle == 0) { + wattron(win_general, A_BOLD); + wbkgdset(win_general, COLOR_PAIR(MAGENTA_ON_NOTHING)); + mvwaddstrf(win_general, 0, 9, "rx: tx: diff: rx: tx: %%:"); + mvwaddstrf(win_general, 1, 9, "rx: tx: err: rx: tx: err: %%:"); + wbkgdset(win_general, COLOR_PAIR(NO_COLOR)); + + wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING)); + mvwaddstrf(win_general, 0, 0, "Host pps "); + mvwaddstrf(win_general, 1, 0, "NICs pps "); + + wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING)); + mvwaddstrf(win_general, 0, 56, "avg"); + mvwaddstrf(win_general, 1, 56, "avg"); + wbkgdset(win_general, COLOR_PAIR(NO_COLOR)); + wattroff(win_general, A_BOLD); + } else { + wattron(win_general, A_BOLD); + wbkgdset(win_general, COLOR_PAIR(BLUE_ON_NOTHING)); + mvwaddstrf(win_general, 0, 9, "rx: tx: rx-tx: tx/rx: rx/tx:"); + mvwaddstrf(win_general, 1, 9, "rx: tx: err: tx/rx: rx/tx:"); + wbkgdset(win_general, COLOR_PAIR(NO_COLOR)); + + wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING)); + mvwaddstrf(win_general, 0, 0, "Host tot "); + mvwaddstrf(win_general, 1, 0, "NICs tot "); + wattroff(win_general, A_BOLD); + } +} + +static void draw_status_bar(void) +{ + wbkgd(win_help, COLOR_PAIR(BLACK_ON_WHITE)); + werase(win_help); + mvwaddstrf(win_help, 0, 0, + "Enter 'help' or command, <ESC> or 'quit' to exit, " + "1-%zu to switch screens and 0 to reset stats, '=' to toggle between per-sec and total stats", + n_screens); + wrefresh(win_help); + mvwin(win_help, LINES - 1, 0); +} + +static void draw_log_window(void) +{ + idlok(win_txt, FALSE); + /* Get scrolling */ + scrollok(win_txt, TRUE); + + /* Leave cursor where it was */ + leaveok(win_txt, TRUE); + wbkgd(win_txt, COLOR_PAIR(BLACK_ON_CYAN)); + wrefresh(win_txt); +} + +static void stats_display_layout(uint8_t in_place) +{ + uint8_t cur_stats_height; + + cur_stats_height = current_screen->get_height(); + cur_stats_height = cur_stats_height > max_n_lines? max_n_lines: cur_stats_height; + + display_lock(); + if (!in_place) { + // moving existing windows does not work + delwin(win_txt); + delwin(win_general); + delwin(win_title); + delwin(win_tabs); + delwin(win_cmd); + delwin(win_txt); + delwin(win_help); + + clear(); + } + + if (!in_place) { + win_stat = create_subwindow(cur_stats_height + 2, 0, 4, 0); + win_tabs = create_subwindow(1, 0, 1, 0); + win_general = create_subwindow(2, 0, 2, 0); + win_title = create_subwindow(1, 0, 0, 0); + win_cmd = create_subwindow(1, 0, cur_stats_height + 2 + 4, 0); + win_txt_height = LINES - cur_stats_height - 2 - 3 - 3; + win_txt = create_subwindow(win_txt_height, 0, cur_stats_height + 4 + 3, 0); + win_help = create_subwindow(1, 0, LINES - 1, 0); + } + + draw_title(); + draw_general_frame(); + /* Command line */ + wbkgd(win_cmd, COLOR_PAIR(BLACK_ON_YELLOW)); + idlok(win_cmd, FALSE); + /* Move cursor at insertion point */ + leaveok(win_cmd, FALSE); + + draw_status_bar(); + draw_log_window(); + + /* Draw everything to the screen */ + refresh(); + current_screen->draw_frame(&screen_state); + display_unlock(); + + refresh_cmd_win(); + display_stats(); +} + +void display_end(void) +{ + pthread_mutex_destroy(&disp_mtx); + + if (scr != NULL) { + endwin(); + } +} + +static void pps_print(WINDOW *dst_scr, int y, int x, uint64_t val, int is_blue) +{ + uint64_t rx_pps_disp = val; + uint64_t rx_pps_disp_frac = 0; + uint32_t ten_pow3 = 0; + static const char *units = " KMG"; + char rx_unit = ' '; + + while (rx_pps_disp > 1000) { + rx_pps_disp /= 1000; + rx_pps_disp_frac = (val - rx_pps_disp*1000) / 10; + val /= 1000; + ten_pow3++; + } + + if (ten_pow3 >= strlen(units)) { + wbkgdset(dst_scr, COLOR_PAIR(RED_ON_NOTHING)); + mvwaddstrf(dst_scr, y, x, "---"); + wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR)); + return; + } + + rx_unit = units[ten_pow3]; + + wattron(dst_scr, A_BOLD); + if (is_blue) { + wbkgdset(dst_scr, COLOR_PAIR(BLUE_ON_NOTHING)); + } + else + wbkgdset(dst_scr, COLOR_PAIR(CYAN_ON_NOTHING)); + + mvwaddstrf(dst_scr, y, x, "%3lu", rx_pps_disp); + if (rx_unit != ' ') { + mvwaddstrf(dst_scr, y, x + 3, ".%02lu", rx_pps_disp_frac); + wattroff(dst_scr, A_BOLD); + wbkgdset(dst_scr, COLOR_PAIR(WHITE_ON_NOTHING)); + wattron(dst_scr, A_BOLD); + mvwaddstrf(dst_scr, y, x + 6, "%c", rx_unit); + wattroff(dst_scr, A_BOLD); + wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR)); + } + else { + mvwaddstrf(dst_scr, y, x + 3, " "); + } + wattroff(dst_scr, A_BOLD); + wbkgdset(dst_scr, COLOR_PAIR(NO_COLOR)); +} + +static void display_stats_general_per_sec(void) +{ + struct global_stats_sample *gsl = stats_get_global_stats(1); + struct global_stats_sample *gsp = stats_get_global_stats(0); + + uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsp->host_rx_packets, gsl->tsc - gsp->tsc); + uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsp->host_tx_packets, gsl->tsc - gsp->tsc); + /* Host: RX, TX, Diff */ + pps_print(win_general, 0, 12, rx_pps, 1); + pps_print(win_general, 0, 25, tx_pps, 1); + + uint64_t diff = 0; + if (rx_pps > tx_pps) + diff = rx_pps - tx_pps; + pps_print(win_general, 0, 40, diff, 1); + + uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsp->nics_rx_packets, gsl->tsc - gsp->tsc); + uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsp->nics_tx_packets, gsl->tsc - gsp->tsc); + uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsp->nics_ierrors, gsl->tsc - gsp->tsc); + uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsp->nics_imissed, gsl->tsc - gsp->tsc); + + /* NIC: RX, TX, Diff */ + pps_print(win_general, 1, 12, nics_rx_pps, 1); + pps_print(win_general, 1, 25, nics_tx_pps, 1); + pps_print(win_general, 1, 40, nics_ierrors + nics_imissed, 1); + + wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING)); + wattron(win_general, A_BOLD); + mvwaddstrf(win_general, 0, 103, "%6.2f", tx_pps > rx_pps? 100 : tx_pps * 100.0 / rx_pps); + wattroff(win_general, A_BOLD); + wbkgdset(win_general, COLOR_PAIR(NO_COLOR)); + + struct global_stats_sample *gsb = stats_get_global_stats_beg(); + if (gsb) { + uint64_t rx_pps = val_to_rate(gsl->host_rx_packets - gsb->host_rx_packets, gsl->tsc - gsb->tsc); + uint64_t tx_pps = val_to_rate(gsl->host_tx_packets - gsb->host_tx_packets, gsl->tsc - gsb->tsc); + + uint64_t nics_rx_pps = val_to_rate(gsl->nics_rx_packets - gsb->nics_rx_packets, gsl->tsc - gsb->tsc); + uint64_t nics_tx_pps = val_to_rate(gsl->nics_tx_packets - gsb->nics_tx_packets, gsl->tsc - gsb->tsc); + uint64_t nics_ierrors = val_to_rate(gsl->nics_ierrors - gsb->nics_ierrors, gsl->tsc - gsb->tsc); + uint64_t nics_imissed = val_to_rate(gsl->nics_imissed - gsb->nics_imissed, gsl->tsc - gsb->tsc); + + pps_print(win_general, 0, 64, rx_pps, 0); + pps_print(win_general, 0, 77, tx_pps, 0); + + pps_print(win_general, 1, 64, nics_rx_pps, 0); + pps_print(win_general, 1, 77, nics_tx_pps, 0); + pps_print(win_general, 1, 91, nics_ierrors + nics_imissed, 0); + + wbkgdset(win_general, COLOR_PAIR(CYAN_ON_NOTHING)); + wattron(win_general, A_BOLD); + uint64_t nics_in = gsl->host_rx_packets - gsb->host_rx_packets + gsl->nics_ierrors - gsb->nics_ierrors + gsl->nics_imissed - gsb->nics_imissed; + uint64_t nics_out = gsl->host_tx_packets - gsb->host_tx_packets; + mvwaddstrf(win_general, 1, 103, "%6.2f", nics_out > nics_in? + 100 : nics_out * 100.0 / nics_in); + wattron(win_general, A_BOLD); + wbkgdset(win_general, COLOR_PAIR(NO_COLOR)); + } +} + +static void display_stats_general_total(void) +{ + struct global_stats_sample *gsl = stats_get_global_stats(1); + + int64_t diff = (int64_t)gsl->host_rx_packets - gsl->host_tx_packets; + uint32_t percent; + + /* Host: RX, TX, Diff */ + mvwaddstrf(win_general, 0, 13, "%16lu", gsl->host_rx_packets); + mvwaddstrf(win_general, 0, 35, "%16lu", gsl->host_tx_packets); + mvwaddstrf(win_general, 0, 60, "%16"PRId64"", diff); + if (gsl->host_rx_packets == 0) + percent = 1000000; + else + percent = gsl->host_tx_packets * 1000000 / gsl->host_rx_packets; + mvwaddstrf(win_general, 0, 88, "%3u.%04u%%", percent / 10000, percent % 10000); + if (gsl->host_tx_packets == 0) + percent = 1000000; + else + percent = gsl->host_rx_packets * 1000000 / gsl->host_tx_packets; + mvwaddstrf(win_general, 0, 106, "%3u.%04u%%", percent / 10000, percent % 10000); + + mvwaddstrf(win_general, 1, 13, "%16lu", gsl->nics_rx_packets); + mvwaddstrf(win_general, 1, 35, "%16lu", gsl->nics_tx_packets); + mvwaddstrf(win_general, 1, 60, "%16lu", gsl->nics_ierrors + gsl->nics_imissed); + if (gsl->nics_rx_packets == 0) + percent = 1000000; + else + percent = gsl->nics_tx_packets * 1000000 / gsl->nics_rx_packets; + mvwaddstrf(win_general, 1, 88, "%3u.%04u%%", percent / 10000, percent % 10000); + if (gsl->nics_tx_packets == 0) + percent = 1000000; + else + percent = gsl->nics_rx_packets * 1000000 / gsl->nics_tx_packets; + mvwaddstrf(win_general, 1, 106, "%3u.%04u%%", percent / 10000, percent % 10000); +} + +static void display_stats_general(void) +{ + /* moment when stats were gathered. */ + uint64_t cur_tsc = stats_get_last_tsc(); + uint64_t up_time = tsc_to_sec(cur_tsc - stats_global_start_tsc()); + uint64_t up_time2 = tsc_to_sec(cur_tsc - stats_global_beg_tsc()); + uint64_t rem_time = -1; + char title_str[128] = {0}; + + if (stats_global_end_tsc()) { + uint64_t rem_tsc = stats_global_end_tsc() > cur_tsc? stats_global_end_tsc() - cur_tsc : 0; + + rem_time = tsc_to_sec(rem_tsc); + } + + if (up_time != up_time2 && cur_tsc >= stats_global_beg_tsc()) { + if (stats_global_end_tsc()) + snprintf(title_str, sizeof(title_str), "%5lu (%lu) up, %lu rem", up_time, up_time2, rem_time); + else + snprintf(title_str, sizeof(title_str), "%5lu (%lu) up", up_time, up_time2); + } + else { + if (stats_global_end_tsc()) + snprintf(title_str, sizeof(title_str), "%5lu up, %lu rem", up_time, rem_time); + else + snprintf(title_str, sizeof(title_str), "%5lu up", up_time); + } + + /* Only print up time information if there is enough space */ + if ((int)((COLS + title_len)/2 + strlen(title_str) + 1) < COLS) { + mvwaddstrf(win_title, 0, COLS - strlen(title_str), "%s", title_str); + wrefresh(win_title); + } + + if (screen_state.toggle == 0) + display_stats_general_per_sec(); + else + display_stats_general_total(); + + wrefresh(win_general); +} + +char *print_time_unit_err_usec(char *dst, struct time_unit_err *t) +{ + uint64_t nsec_total = time_unit_to_nsec(&t->time); + + uint64_t usec = nsec_total/1000; + uint64_t nsec = nsec_total - usec*1000; + + uint64_t nsec_total_error = time_unit_to_nsec(&t->error); + + uint64_t usec_error = nsec_total_error/1000; + uint64_t nsec_error = nsec_total_error - usec_error*1000; + + sprintf(dst, "%4"PRIu64".%03"PRIu64" +/- %2"PRIu64".%03"PRIu64"", usec, nsec, usec_error, nsec_error); + return dst; +} + +char *print_time_unit_usec(char *dst, struct time_unit *t) +{ + uint64_t nsec_total = time_unit_to_nsec(t); + + uint64_t usec = nsec_total/1000; + uint64_t nsec = nsec_total - usec*1000; + + sprintf(dst, "%4"PRIu64".%03"PRIu64"", usec, nsec); + return dst; +} + +void toggle_display_screen(void) +{ + screen_state.toggle = !screen_state.toggle; + stats_display_layout(0); +} + +void display_screen(unsigned screen_id) +{ + if (screen_id >= n_screens) { + plog_err("Unsupported screen %d\n", screen_id + 1); + return; + } + + if (screen_state.chosen_screen == screen_id) { + stats_display_layout(1); + } + else { + screen_state.chosen_screen = screen_id; + current_screen = display_screens[screen_id]; + stats_display_layout(0); + } +} + +void display_page_up(void) +{ +} + +void display_page_down(void) +{ +} + +void display_refresh(void) +{ + stats_display_layout(1); +} + +void display_stats(void) +{ + display_lock(); + current_screen->draw_stats(&screen_state); + display_stats_general(); + wrefresh(win_stat); + display_unlock(); +} + +static char pages[32768] = {0}; +static int cur_idx = 0; +static size_t pages_len = 0; + +void display_print_page(void) +{ + int n_lines = 0; + int cur_idx_prev = cur_idx; + + if (cur_idx >= (int)pages_len) { + return; + } + + display_lock(); + for (size_t i = cur_idx; i < pages_len; ++i) { + if (pages[i] == '\n') { + n_lines++; + if (n_lines == win_txt_height - 2) { + pages[i] = 0; + cur_idx = i + 1; + break; + } + } + } + + waddstr(win_txt, pages + cur_idx_prev); + if (cur_idx != cur_idx_prev && cur_idx < (int)pages_len) + waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n"); + else { + pages_len = 0; + } + wrefresh(win_txt); + display_unlock(); +} + +void display_print(const char *str) +{ + display_lock(); + + if (scr == NULL) { + fputs(str, stdout); + fflush(stdout); + display_unlock(); + return; + } + + /* Check if the whole string can fit on the screen. */ + pages_len = strlen(str); + int n_lines = 0; + memset(pages, 0, sizeof(pages)); + memcpy(pages, str, pages_len); + cur_idx = 0; + for (size_t i = 0; i < pages_len; ++i) { + if (pages[i] == '\n') { + n_lines++; + if (n_lines == win_txt_height - 2) { + pages[i] = 0; + cur_idx = i + 1; + break; + } + } + } + + waddstr(win_txt, pages); + if (cur_idx != 0) + waddstr(win_txt, "\nPRESS ENTER FOR MORE...\n"); + else + pages_len = 0; + + wrefresh(win_txt); + display_unlock(); +} diff --git a/VNFs/DPPD-PROX/display.h b/VNFs/DPPD-PROX/display.h new file mode 100644 index 00000000..4b517546 --- /dev/null +++ b/VNFs/DPPD-PROX/display.h @@ -0,0 +1,109 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _DISPLAY_H_ +#define _DISPLAY_H_ + +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> + +#include "display_latency.h" +#include "stats_cons.h" +#include "clock.h" + +struct display_column { + char title[32]; + int offset; + int width; + struct display_page *display_page; +}; + +struct display_table { + struct display_column cols[16]; + char title[32]; + int n_cols; + int offset; + int width; +}; + +struct display_page { + struct display_table tables[8]; + int n_tables; + int width; +}; + +struct screen_state { + unsigned chosen_screen; + unsigned chosen_page; + int toggle; + int pps_unit; +}; + +struct display_screen { + void (*draw_frame)(struct screen_state *screen_state); + void (*draw_stats)(struct screen_state *screen_state); + int (*get_height)(void); + const char *title; +}; + +void display_set_pps_unit(int val); + +struct lcore_cfg; +struct task_args; + +void display_page_draw_frame(const struct display_page *display_page, int height); +int display_column_get_width(const struct display_column *display_column); +void display_column_init(struct display_column *display_column, const char *title, unsigned width); +struct display_column *display_table_add_col(struct display_table *table); +void display_table_init(struct display_table *table, const char *title); +struct display_table *display_page_add_table(struct display_page *display_page); +void display_page_init(struct display_page *display_page); +__attribute__((format(printf, 3, 4))) void display_column_print(const struct display_column *display_column, int row, const char *fmt, ...); +void display_column_print_core_task(const struct display_column *display_column, int row, struct lcore_cfg *lconf, struct task_args *targ); +void display_column_print_number(const struct display_column *display_column, int row, uint64_t number); + +char *print_time_unit_err_usec(char *dst, struct time_unit_err *t); +char *print_time_unit_usec(char *dst, struct time_unit *t); +struct port_queue; +struct rte_ring; +void display_column_port_ring(const struct display_column *display_column, int row, struct port_queue *ports, int port_count, struct rte_ring **rings, int ring_count); + +void display_init(void); +void display_end(void); +void display_stats(void); +void display_refresh(void); +void display_print(const char *str); +void display_cmd(const char *cmd, int cmd_len, int cursor_pos); +void display_screen(unsigned screen_id); +void toggle_display_screen(void); +void display_page_up(void); +void display_page_down(void); +void display_print_page(void); +void display_lock(void); +void display_unlock(void); + +int display_getch(void); + +static struct stats_cons display = { + .init = display_init, + .notify = display_stats, + .refresh = display_refresh, + .finish = display_end, + .flags = STATS_CONS_F_ALL, +}; + +#endif /* _DISPLAY_H_ */ diff --git a/VNFs/DPPD-PROX/display_l4gen.c b/VNFs/DPPD-PROX/display_l4gen.c new file mode 100644 index 00000000..7cc1f5f3 --- /dev/null +++ b/VNFs/DPPD-PROX/display_l4gen.c @@ -0,0 +1,172 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "display.h" +#include "display_l4gen.h" +#include "stats_l4gen.h" + +static struct display_page display_page_l4gen; + +static struct display_column *core_col; +static struct display_column *tcp_setup_col; +static struct display_column *udp_setup_col; +static struct display_column *all_setup_col; +static struct display_column *bundles_setup_col; +static struct display_column *tcp_teardown_col; +static struct display_column *tcp_teardown_retx_col; +static struct display_column *udp_teardown_col; +static struct display_column *tcp_expire_col; +static struct display_column *udp_expire_col; +static struct display_column *active_col; +static struct display_column *retx_col; + +static void display_l4gen_draw_frame(struct screen_state *state) +{ + const uint32_t n_l4gen = stats_get_n_l4gen(); + + display_page_init(&display_page_l4gen); + + struct display_table *core = display_page_add_table(&display_page_l4gen); + struct display_table *setup_rate = display_page_add_table(&display_page_l4gen); + struct display_table *teardown_rate = display_page_add_table(&display_page_l4gen); + struct display_table *expire_rate = display_page_add_table(&display_page_l4gen); + struct display_table *other = display_page_add_table(&display_page_l4gen); + + display_table_init(core, "Core"); + display_table_init(setup_rate, "Setup rate (flows/s)"); + display_table_init(teardown_rate, "Teardown rate (flows/s)"); + display_table_init(expire_rate, "Expire rate (flows/s)"); + display_table_init(other, "Other"); + + core_col = display_table_add_col(core); + display_column_init(core_col, "Nb", 4); + + tcp_setup_col = display_table_add_col(setup_rate); + display_column_init(tcp_setup_col, "TCP", 10); + udp_setup_col = display_table_add_col(setup_rate); + display_column_init(udp_setup_col, "UDP", 10); + all_setup_col = display_table_add_col(setup_rate); + display_column_init(all_setup_col, "TCP + UDP", 9); + bundles_setup_col = display_table_add_col(setup_rate); + display_column_init(bundles_setup_col, "Bundles", 9); + + tcp_teardown_col = display_table_add_col(teardown_rate); + display_column_init(tcp_teardown_col, "TCP w/o reTX", 12); + tcp_teardown_retx_col = display_table_add_col(teardown_rate); + display_column_init(tcp_teardown_retx_col, "TCP w/ reTX", 12); + udp_teardown_col = display_table_add_col(teardown_rate); + display_column_init(udp_teardown_col, "UDP", 12); + + tcp_expire_col = display_table_add_col(expire_rate); + display_column_init(tcp_expire_col, "TCP", 10); + udp_expire_col = display_table_add_col(expire_rate); + display_column_init(udp_expire_col, "TCP", 10); + + active_col = display_table_add_col(other); + display_column_init(active_col, "Active (#)", 10); + retx_col = display_table_add_col(other); + display_column_init(retx_col, "reTX (/s)", 10); + + display_page_draw_frame(&display_page_l4gen, n_l4gen); + + for (uint16_t i = 0; i < n_l4gen; ++i) { + struct task_l4_stats *tls = stats_get_l4_stats(i); + + display_column_print(core_col, i, "%2u/%1u", tls->lcore_id, tls->task_id); + } +} + +static void display_l4gen_draw_stats_line(int row, struct l4_stats_sample *clast, struct l4_stats_sample *cprev) +{ + struct l4_stats *last = &clast->stats; + struct l4_stats *prev = &cprev->stats; + + uint64_t delta_t = clast->tsc - cprev->tsc; + + uint64_t tcp_created = last->tcp_created - prev->tcp_created; + uint64_t udp_created = last->udp_created - prev->udp_created; + + uint64_t tcp_finished_no_retransmit = last->tcp_finished_no_retransmit - prev->tcp_finished_no_retransmit; + uint64_t tcp_finished_retransmit = last->tcp_finished_retransmit - prev->tcp_finished_retransmit; + uint64_t tcp_expired = last->tcp_expired - prev->tcp_expired; + uint64_t tcp_retransmits = last->tcp_retransmits - prev->tcp_retransmits; + uint64_t udp_finished = last->udp_finished - prev->udp_finished; + uint64_t udp_expired = last->udp_expired - prev->udp_expired; + uint64_t bundles_created = last->bundles_created - prev->bundles_created; + + uint64_t tcp_setup_rate = val_to_rate(tcp_created, delta_t); + uint64_t udp_setup_rate = val_to_rate(udp_created, delta_t); + uint64_t all_setup_rate = val_to_rate(tcp_created + udp_created, delta_t); + uint64_t bundle_setup_rate = val_to_rate(bundles_created, delta_t); + + uint64_t tcp_teardown_rate = val_to_rate(tcp_finished_no_retransmit, delta_t); + uint64_t tcp_teardown_retx_rate = val_to_rate(tcp_finished_retransmit, delta_t); + uint64_t udp_teardown_rate = val_to_rate(udp_finished, delta_t); + + uint64_t tcp_expire_rate = val_to_rate(tcp_expired, delta_t); + uint64_t udp_expire_rate = val_to_rate(udp_expired, delta_t); + + display_column_print(tcp_setup_col, row, "%"PRIu64"", tcp_setup_rate); + display_column_print(udp_setup_col, row, "%"PRIu64"", udp_setup_rate); + display_column_print(all_setup_col, row, "%"PRIu64"", all_setup_rate); + display_column_print(bundles_setup_col, row, "%"PRIu64"", bundle_setup_rate); + + display_column_print(tcp_teardown_col, row, "%"PRIu64"", tcp_teardown_rate); + display_column_print(tcp_teardown_retx_col, row, "%"PRIu64"", tcp_teardown_retx_rate); + display_column_print(udp_teardown_col, row, "%"PRIu64"", udp_teardown_rate); + + display_column_print(tcp_expire_col, row, "%"PRIu64"", tcp_expire_rate); + display_column_print(udp_expire_col, row, "%"PRIu64"", udp_expire_rate); + + uint64_t tot_created = last->tcp_created + last->udp_created; + uint64_t tot_finished = last->tcp_finished_retransmit + last->tcp_finished_no_retransmit + + last->udp_finished + last->udp_expired + last->tcp_expired; + + uint64_t active = tot_created - tot_finished; + uint64_t retx = tcp_retransmits; + + display_column_print(active_col, row, "%10"PRIu64"", active); + display_column_print(retx_col, row, "%10"PRIu64"", retx); +} + +static void display_l4gen_draw_stats(struct screen_state *state) +{ + const uint32_t n_l4gen = stats_get_n_l4gen(); + + for (uint16_t i = 0; i < n_l4gen; ++i) { + struct l4_stats_sample *clast = stats_get_l4_stats_sample(i, 1); + struct l4_stats_sample *cprev = stats_get_l4_stats_sample(i, 0); + + display_l4gen_draw_stats_line(i, clast, cprev); + } +} + +static int display_l4gen_get_height(void) +{ + return stats_get_n_l4gen(); +} + +static struct display_screen display_screen_l4gen = { + .draw_frame = display_l4gen_draw_frame, + .draw_stats = display_l4gen_draw_stats, + .get_height = display_l4gen_get_height, + .title = "l4gen", +}; + +struct display_screen *display_l4gen(void) +{ + return &display_screen_l4gen; +} diff --git a/VNFs/DPPD-PROX/display_l4gen.h b/VNFs/DPPD-PROX/display_l4gen.h new file mode 100644 index 00000000..24b6c5af --- /dev/null +++ b/VNFs/DPPD-PROX/display_l4gen.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_L4GEN_H +#define DISPLAY_L4GEN_H + +struct display_screen; +struct display_screen *display_l4gen(void); + +#endif /* DISPLAY_L4GEN_H */ diff --git a/VNFs/DPPD-PROX/display_latency.c b/VNFs/DPPD-PROX/display_latency.c new file mode 100644 index 00000000..04382e46 --- /dev/null +++ b/VNFs/DPPD-PROX/display_latency.c @@ -0,0 +1,154 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "display.h" +#include "display_latency.h" +#include "stats_latency.h" +#include "lconf.h" + +static struct display_column *min_col; +static struct display_column *max_col; +static struct display_column *avg_col; +static struct display_column *stddev_col; +static struct display_column *accuracy_limit_col; +static struct display_column *used_col; +static struct display_column *lost_col; +static struct display_page display_page_latency; + +static void display_latency_draw_frame(struct screen_state *screen_state) +{ + const uint32_t n_latency = stats_get_n_latency(); + struct display_column *core_col; + struct display_column *port_col; + + display_page_init(&display_page_latency); + + struct display_table *core = display_page_add_table(&display_page_latency); + struct display_table *port = display_page_add_table(&display_page_latency); + struct display_table *lat = display_page_add_table(&display_page_latency); + struct display_table *acc = display_page_add_table(&display_page_latency); + struct display_table *other = display_page_add_table(&display_page_latency); + + display_table_init(core, "Core"); + core_col = display_table_add_col(core); + display_column_init(core_col, "Nb", 4); + + display_table_init(port, "Port Nb"); + port_col = display_table_add_col(port); + display_column_init(port_col, "RX", 8); + + if (screen_state->toggle == 0) + display_table_init(lat, "Measured Latency per interval"); + else + display_table_init(lat, "Measured Latency since reset"); + + min_col = display_table_add_col(lat); + display_column_init(min_col, "Min (us)", 20); + max_col = display_table_add_col(lat); + display_column_init(max_col, "Max (us)", 20); + avg_col = display_table_add_col(lat); + display_column_init(avg_col, "Avg (us)", 20); + stddev_col = display_table_add_col(lat); + display_column_init(stddev_col, "Stddev (us)", 20); + + display_table_init(acc, "Accuracy "); + used_col = display_table_add_col(acc); + display_column_init(used_col, "Used Packets (%)", 16); + accuracy_limit_col = display_table_add_col(acc); + display_column_init(accuracy_limit_col, "limit (us)", 16); + + display_table_init(other, "Other"); + + lost_col = display_table_add_col(other); + display_column_init(lost_col, "Lost Packets", 16); + + display_page_draw_frame(&display_page_latency, n_latency); + + for (uint16_t i = 0; i < n_latency; ++i) { + uint32_t lcore_id = stats_latency_get_core_id(i); + uint32_t task_id = stats_latency_get_task_id(i); + struct task_args *targ = &lcore_cfg[lcore_id].targs[task_id]; + + display_column_print(core_col, i, "%2u/%1u", lcore_id, task_id); + display_column_port_ring(port_col, i, targ->rx_port_queue, targ->nb_rxports, targ->rx_rings, targ->nb_rxrings); + } +} + +#define AFTER_POINT 1000000 + +static void display_stats_latency_entry(int row, struct stats_latency *stats_latency) +{ + struct time_unit_err avg = stats_latency->avg; + struct time_unit_err min = stats_latency->min; + struct time_unit_err max = stats_latency->max; + struct time_unit_err stddev = stats_latency->stddev; + struct time_unit accuracy_limit = stats_latency->accuracy_limit; + + uint32_t used = 0; + + if (stats_latency->tot_all_packets) + used = stats_latency->tot_packets * (100 * AFTER_POINT) / stats_latency->tot_all_packets; + + char dst[32]; + + if (stats_latency->tot_packets) { + display_column_print(min_col, row, "%s", print_time_unit_err_usec(dst, &min)); + display_column_print(max_col, row, "%s", print_time_unit_err_usec(dst, &max)); + display_column_print(avg_col, row, "%s", print_time_unit_err_usec(dst, &avg)); + display_column_print(stddev_col, row, "%s", print_time_unit_err_usec(dst, &stddev)); + } else { + display_column_print(min_col, row, "%s", "N/A"); + display_column_print(max_col, row, "%s", "N/A"); + display_column_print(avg_col, row, "%s", "N/A"); + display_column_print(stddev_col, row, "%s", "N/A"); + } + + display_column_print(accuracy_limit_col, row, "%s", print_time_unit_usec(dst, &accuracy_limit)); + display_column_print(lost_col, row, "%16"PRIu64"", stats_latency->lost_packets); + display_column_print(used_col, row, "%3u.%06u", used / AFTER_POINT, used % AFTER_POINT); +} + +static void display_latency_draw_stats(struct screen_state *screen_state) +{ + const uint32_t n_latency = stats_get_n_latency(); + struct stats_latency *stats_latency; + + for (uint16_t i = 0; i < n_latency; ++i) { + if (screen_state->toggle == 0) + stats_latency = stats_latency_get(i); + else + stats_latency = stats_latency_tot_get(i); + + display_stats_latency_entry(i, stats_latency); + } +} + +static int display_latency_get_height(void) +{ + return stats_get_n_latency(); +} + +static struct display_screen display_screen_latency = { + .draw_frame = display_latency_draw_frame, + .draw_stats = display_latency_draw_stats, + .get_height = display_latency_get_height, + .title = "latency", +}; + +struct display_screen *display_latency(void) +{ + return &display_screen_latency; +} diff --git a/VNFs/DPPD-PROX/display_latency.h b/VNFs/DPPD-PROX/display_latency.h new file mode 100644 index 00000000..0821b2d9 --- /dev/null +++ b/VNFs/DPPD-PROX/display_latency.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_LATENCY_H +#define DISPLAY_LATENCY_H + +struct display_screen; +struct display_screen *display_latency(void); + +#endif /* DISPLAY_LATENCY_H */ diff --git a/VNFs/DPPD-PROX/display_mempools.c b/VNFs/DPPD-PROX/display_mempools.c new file mode 100644 index 00000000..2982104b --- /dev/null +++ b/VNFs/DPPD-PROX/display_mempools.c @@ -0,0 +1,111 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "display_mempools.h" +#include "stats_mempool.h" +#include "display.h" +#include "defaults.h" + +static struct display_page display_page_mempools; +static struct display_column *nb_col; +static struct display_column *queue_col; +static struct display_column *occup_col; +static struct display_column *used_col; +static struct display_column *free_col; +static struct display_column *total_col; +static struct display_column *mem_used_col; +static struct display_column *mem_free_col; +static struct display_column *mem_tot_col; + +static void display_mempools_draw_frame(struct screen_state *screen_state) +{ + const uint32_t n_mempools = stats_get_n_mempools(); + + display_page_init(&display_page_mempools); + + struct display_table *port = display_page_add_table(&display_page_mempools); + struct display_table *stats = display_page_add_table(&display_page_mempools); + + display_table_init(port, "Port"); + display_table_init(stats, "Sampled statistics"); + + nb_col = display_table_add_col(port); + queue_col = display_table_add_col(port); + display_column_init(nb_col, "Nb", 4); + display_column_init(queue_col, "Queue", 5); + + occup_col = display_table_add_col(stats); + display_column_init(occup_col, "Occup (%)", 9); + used_col = display_table_add_col(stats); + display_column_init(used_col, "Used (#)", 12); + free_col = display_table_add_col(stats); + display_column_init(free_col, "Free (#)", 12); + total_col = display_table_add_col(stats); + display_column_init(total_col, "Total (#)", 13); + + mem_used_col = display_table_add_col(stats); + display_column_init(mem_used_col, "Mem Used (KB)", 13); + mem_free_col = display_table_add_col(stats); + display_column_init(mem_free_col, "Mem Free (KB)", 13); + mem_tot_col = display_table_add_col(stats); + display_column_init(mem_tot_col, "Mem Tot (KB)", 12); + + display_page_draw_frame(&display_page_mempools, n_mempools); + + for (uint16_t i = 0; i < n_mempools; ++i) { + struct mempool_stats *ms = stats_get_mempool_stats(i); + + display_column_print(nb_col, i, "%4u", ms->port); + display_column_print(queue_col, i, "%5u", ms->queue); + display_column_print(total_col, i, "%13zu", ms->size); + display_column_print(mem_tot_col, i, "%12zu", ms->size * MBUF_SIZE/1024); + } +} + +static void display_mempools_draw_stats(struct screen_state *state) +{ + const uint32_t n_mempools = stats_get_n_mempools(); + + for (uint16_t i = 0; i < n_mempools; ++i) { + struct mempool_stats *ms = stats_get_mempool_stats(i); + const size_t used = ms->size - ms->free; + const uint32_t used_frac = used*10000/ms->size; + + display_column_print(occup_col, i, "%6u.%02u", used_frac/100, used_frac % 100); + display_column_print(used_col, i, "%12zu", used); + display_column_print(free_col, i, "%12zu", ms->free); + + display_column_print(mem_free_col, i, "%13zu", used * MBUF_SIZE/1024); + display_column_print(mem_used_col, i, "%13zu", ms->free * MBUF_SIZE/1024); + } +} + +static int display_mempools_get_height(void) +{ + return stats_get_n_mempools(); +} + +static struct display_screen display_screen_mempools = { + .draw_frame = display_mempools_draw_frame, + .draw_stats = display_mempools_draw_stats, + .get_height = display_mempools_get_height, + .title = "mempools", +}; + +struct display_screen *display_mempools(void) +{ + return &display_screen_mempools; +} diff --git a/VNFs/DPPD-PROX/display_mempools.h b/VNFs/DPPD-PROX/display_mempools.h new file mode 100644 index 00000000..b5c4d99c --- /dev/null +++ b/VNFs/DPPD-PROX/display_mempools.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_MEMPOOLS_H +#define DISPLAY_MEMPOOLS_H + +struct display_screen; +struct display_screen *display_mempools(void); + +#endif /* DISPLAY_MEMPOOLS_H */ diff --git a/VNFs/DPPD-PROX/display_pkt_len.c b/VNFs/DPPD-PROX/display_pkt_len.c new file mode 100644 index 00000000..df34616a --- /dev/null +++ b/VNFs/DPPD-PROX/display_pkt_len.c @@ -0,0 +1,138 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "prox_globals.h" +#include "display_pkt_len.h" +#include "stats_port.h" +#include "display.h" +#include "defaults.h" +#include "prox_port_cfg.h" +#include "clock.h" + +static struct display_page display_page_pkt_len; +static struct display_column *port_col; +static struct display_column *name_col; +static struct display_column *type_col; +static struct display_column *stats_col[PKT_SIZE_COUNT]; + +const char *titles[] = { + "64B (#)", + "65-127B (#)", + "128-255B (#)", + "256-511B (#)", + "512-1023B (#)", + "1024-1522B (#)", + "1523B+ (#)", +}; + +static int port_disp[PROX_MAX_PORTS]; +static int n_port_disp; + +static void display_pkt_len_draw_frame(struct screen_state *screen_state) +{ + n_port_disp = 0; + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + if (prox_port_cfg[i].active) { + port_disp[n_port_disp++] = i; + } + } + + display_page_init(&display_page_pkt_len); + + struct display_table *port_name = display_page_add_table(&display_page_pkt_len); + + display_table_init(port_name, "Port"); + port_col = display_table_add_col(port_name); + name_col = display_table_add_col(port_name); + type_col = display_table_add_col(port_name); + + display_column_init(port_col, "ID", 4); + display_column_init(name_col, "Name", 8); + display_column_init(type_col, "Type", 7); + + struct display_table *stats = display_page_add_table(&display_page_pkt_len); + + if (screen_state->toggle == 0) + display_table_init(stats, "Statistics per second"); + else + display_table_init(stats, "Total Statistics"); + + for (int i = 0; i < PKT_SIZE_COUNT; ++i) { + stats_col[i] = display_table_add_col(stats); + display_column_init(stats_col[i], titles[i], 13); + } + + display_page_draw_frame(&display_page_pkt_len, n_port_disp); + + for (uint8_t i = 0; i < n_port_disp; ++i) { + const uint32_t port_id = port_disp[i]; + + display_column_print(port_col, i, "%4u", port_id); + display_column_print(name_col, i, "%8s", prox_port_cfg[port_id].name); + display_column_print(type_col, i, "%7s", prox_port_cfg[port_id].short_name); + } +} + +static void display_pkt_len_draw_stats(struct screen_state *state) +{ + for (uint8_t i = 0; i < n_port_disp; ++i) { + const uint32_t port_id = port_disp[i]; + struct port_stats_sample *last = stats_get_port_stats_sample(port_id, 1); + struct port_stats_sample *prev = stats_get_port_stats_sample(port_id, 0); + + uint64_t delta_t = last->tsc - prev->tsc; + if (delta_t == 0) // This could happen if we just reset the screen => stats will be updated later + continue; + + if (state->toggle == 0) { + uint64_t diff; + + for (int j = 0; j < PKT_SIZE_COUNT; ++j) { + if (last->tx_pkt_size[j] == (uint64_t)-1) { + display_column_print(stats_col[j], i, " --- "); + } else { + diff = last->tx_pkt_size[j] - prev->tx_pkt_size[j]; + display_column_print(stats_col[j], i, "%13lu", val_to_rate(diff, delta_t)); + } + } + } else { + for (int j = 0; j < PKT_SIZE_COUNT; ++j) { + if (last->tx_pkt_size[j] == (uint64_t)-1) { + display_column_print(stats_col[j], i, " --- "); + } else { + display_column_print(stats_col[j], i, "%13lu", last->tx_pkt_size[j]); + } + } + } + } +} + +static int display_pkt_len_get_height(void) +{ + return stats_get_n_ports(); +} + +static struct display_screen display_screen_pkt_len = { + .draw_frame = display_pkt_len_draw_frame, + .draw_stats = display_pkt_len_draw_stats, + .get_height = display_pkt_len_get_height, + .title = "pkt_len", +}; + +struct display_screen *display_pkt_len(void) +{ + return &display_screen_pkt_len; +} diff --git a/VNFs/DPPD-PROX/display_pkt_len.h b/VNFs/DPPD-PROX/display_pkt_len.h new file mode 100644 index 00000000..2c7af420 --- /dev/null +++ b/VNFs/DPPD-PROX/display_pkt_len.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_PKT_LEN_H +#define DISPLAY_PKT_LEN_H + +struct display_screen; +struct display_screen *display_pkt_len(void); + +#endif /* DISPLAY_PKT_LEN_H */ diff --git a/VNFs/DPPD-PROX/display_ports.c b/VNFs/DPPD-PROX/display_ports.c new file mode 100644 index 00000000..b1027f93 --- /dev/null +++ b/VNFs/DPPD-PROX/display_ports.c @@ -0,0 +1,252 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "clock.h" +#include "display_ports.h" +#include "display.h" +#include "stats_port.h" +#include "prox_globals.h" +#include "prox_port_cfg.h" + +static struct display_page display_page_ports; +static struct display_column *nb_col; +static struct display_column *name_col; +static struct display_column *type_col; + +static struct display_column *no_mbufs_col; +static struct display_column *ierrors_col; +static struct display_column *imissed_col; +static struct display_column *oerrors_col; +static struct display_column *rx_col; +static struct display_column *tx_col; +static struct display_column *rx_bytes_col; +static struct display_column *tx_bytes_col; +static struct display_column *rx_percent_col; +static struct display_column *tx_percent_col; + +static int port_disp[PROX_MAX_PORTS]; +static int n_port_disp; + +static void display_ports_draw_frame(struct screen_state *state) +{ + n_port_disp = 0; + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + if (prox_port_cfg[i].active) { + port_disp[n_port_disp++] = i; + } + } + + const uint32_t n_ports = stats_get_n_ports(); + char name[32]; + char *ptr; + + display_page_init(&display_page_ports); + + struct display_table *port = display_page_add_table(&display_page_ports); + struct display_table *stats = display_page_add_table(&display_page_ports); + + display_table_init(port, "Port"); + + nb_col = display_table_add_col(port); + name_col = display_table_add_col(port); + type_col = display_table_add_col(port); + + display_column_init(nb_col, "Nb", 4); + display_column_init(name_col, "Name", 8); + display_column_init(type_col, "Type", 7); + + if (state->toggle == 0) { + display_table_init(stats, "Statistics per second"); + no_mbufs_col = display_table_add_col(stats); + ierrors_col = display_table_add_col(stats); + imissed_col = display_table_add_col(stats); + oerrors_col = display_table_add_col(stats); + rx_col = display_table_add_col(stats); + tx_col = display_table_add_col(stats); + rx_bytes_col = display_table_add_col(stats); + tx_bytes_col = display_table_add_col(stats); + rx_percent_col = display_table_add_col(stats); + tx_percent_col = display_table_add_col(stats); + + display_column_init(no_mbufs_col, "no mbufs (#)", 12); + display_column_init(ierrors_col, "ierrors (#)", 12); + display_column_init(imissed_col, "imissed (#)", 12); + display_column_init(oerrors_col, "oerrors (#)", 12); + display_column_init(rx_col, "RX (Kpps)", 10); + display_column_init(tx_col, "TX (Kpps)", 10); + display_column_init(rx_bytes_col, "RX (Kbps)", 10); + display_column_init(tx_bytes_col, "TX (Kbps)", 10); + display_column_init(rx_percent_col, "RX (%)", 8); + display_column_init(tx_percent_col, "TX (%)", 8); + } else { + display_table_init(stats, "Total statistics"); + no_mbufs_col = display_table_add_col(stats); + ierrors_col = display_table_add_col(stats); + imissed_col = display_table_add_col(stats); + oerrors_col = display_table_add_col(stats); + rx_col = display_table_add_col(stats); + tx_col = display_table_add_col(stats); + + display_column_init(no_mbufs_col, "no mbufs (#)", 13); + display_column_init(ierrors_col, "ierrors (#)", 13); + display_column_init(imissed_col, "imissed (#)", 13); + display_column_init(oerrors_col, "oerrors (#)", 13); + display_column_init(rx_col, "RX (#)", 13); + display_column_init(tx_col, "TX (#)", 13); + } + + display_page_draw_frame(&display_page_ports, n_port_disp); + for (uint8_t i = 0; i < n_port_disp; ++i) { + const uint32_t port_id = port_disp[i]; + + display_column_print(nb_col, i, "%u", port_id); + display_column_print(name_col, i, "%s", prox_port_cfg[port_id].name); + display_column_print(type_col, i, "%s", prox_port_cfg[port_id].short_name); + } +} + +struct percent { + uint32_t percent; + uint32_t part; +}; + +static struct percent calc_percent(uint64_t val, uint64_t delta_t) +{ + struct percent ret; + uint64_t normalized = 0; + + if (val == 0) { + ret.percent = 0; + ret.part = 0; + } else if (val < thresh) { + ret.percent = val * tsc_hz / delta_t / 12500000; + ret.part = (val * tsc_hz / delta_t / 1250) % 10000; + } else if (delta_t > tsc_hz) { + ret.percent = val / (delta_t / tsc_hz) / 12500000; + ret.part = (val / (delta_t / tsc_hz) / 1250) % 10000; + } else { + ret.percent = 0; + ret.part = 0; + } + return ret; +} + +static void display_ports_draw_per_sec_stats(void) +{ + for (uint8_t i = 0; i < n_port_disp; ++i) { + const uint32_t port_id = port_disp[i]; + struct port_stats_sample *last = stats_get_port_stats_sample(port_id, 1); + struct port_stats_sample *prev = stats_get_port_stats_sample(port_id, 0); + + uint64_t delta_t = last->tsc - prev->tsc; + + /* This could happen if we just reset the screen. + stats will be updated later */ + if (delta_t == 0) + continue; + + uint64_t no_mbufs_rate = val_to_rate(last->no_mbufs - prev->no_mbufs, delta_t); + uint64_t ierrors_rate = val_to_rate(last->ierrors - prev->ierrors, delta_t); + uint64_t imissed_rate = val_to_rate(last->imissed - prev->imissed, delta_t); + uint64_t oerrors_rate = val_to_rate(last->oerrors - prev->oerrors, delta_t); + + uint64_t rx_kbps_rate = val_to_rate((last->rx_bytes - prev->rx_bytes) * 8, delta_t) / 1000; + uint64_t tx_kbps_rate = val_to_rate((last->tx_bytes - prev->tx_bytes) * 8, delta_t) / 1000; + + uint64_t rx_rate = val_to_rate(last->rx_tot - prev->rx_tot, delta_t) / 1000; + if (unlikely(prev->rx_tot > last->rx_tot)) + rx_rate = 0; + uint64_t tx_rate = val_to_rate(last->tx_tot - prev->tx_tot, delta_t) / 1000; + if (unlikely(prev->tx_tot > last->tx_tot)) + tx_rate = 0; + + /* Take 20 bytes overhead (or 24 if crc strip is enabled) into accound */ + struct percent rx_percent; + struct percent tx_percent; + if (strcmp(prox_port_cfg[port_id].short_name, "i40e") == 0) { + if (prox_port_cfg[port_id].port_conf.rxmode.hw_strip_crc == 1) { + rx_percent = calc_percent(last->rx_bytes - prev->rx_bytes + 24 * (last->rx_tot - prev->rx_tot), delta_t); + tx_percent = calc_percent(last->tx_bytes - prev->tx_bytes + 24 * (last->tx_tot - prev->tx_tot), delta_t); + } else { + rx_percent = calc_percent(last->rx_bytes - prev->rx_bytes + 20 * (last->rx_tot - prev->rx_tot), delta_t); + tx_percent = calc_percent(last->tx_bytes - prev->tx_bytes + 20 * (last->tx_tot - prev->tx_tot), delta_t); + } + } else { + if (prox_port_cfg[port_id].port_conf.rxmode.hw_strip_crc == 1) { + rx_percent = calc_percent(last->rx_bytes - prev->rx_bytes + 24 * (last->rx_tot - prev->rx_tot), delta_t); + tx_percent = calc_percent(last->tx_bytes - prev->tx_bytes + 24 * (last->tx_tot - prev->tx_tot), delta_t); + } else { + rx_percent = calc_percent(last->rx_bytes - prev->rx_bytes + 20 * (last->rx_tot - prev->rx_tot), delta_t); + tx_percent = calc_percent(last->tx_bytes - prev->tx_bytes + 20 * (last->tx_tot - prev->tx_tot), delta_t); + } + } + + display_column_print(no_mbufs_col, i, "%lu", no_mbufs_rate); + display_column_print(ierrors_col, i, "%lu", ierrors_rate); + display_column_print(imissed_col, i, "%lu", imissed_rate); + display_column_print(oerrors_col, i, "%lu", oerrors_rate); + + display_column_print(rx_bytes_col, i, "%lu", rx_kbps_rate); + display_column_print(tx_bytes_col, i, "%lu", tx_kbps_rate); + display_column_print(rx_col, i, "%lu", rx_rate); + display_column_print(tx_col, i, "%lu", tx_rate); + + display_column_print(rx_percent_col, i, "%3u.%04u", rx_percent.percent, rx_percent.part); + display_column_print(tx_percent_col, i, "%3u.%04u", tx_percent.percent, tx_percent.part); + } +} + +static void display_ports_draw_total_stats(void) +{ + for (uint8_t i = 0; i < n_port_disp; ++i) { + const uint32_t port_id = port_disp[i]; + struct port_stats_sample *last = stats_get_port_stats_sample(port_id, 1); + + display_column_print(no_mbufs_col, i, "%lu", last->no_mbufs); + display_column_print(ierrors_col, i, "%lu", last->ierrors); + display_column_print(imissed_col, i, "%lu", last->imissed); + display_column_print(oerrors_col, i, "%lu", last->oerrors); + display_column_print(rx_col, i, "%lu", last->rx_tot); + display_column_print(tx_col, i, "%lu", last->tx_tot); + } +} + +static void display_ports_draw_stats(struct screen_state *state) +{ + if (state->toggle == 0) + display_ports_draw_per_sec_stats(); + else + display_ports_draw_total_stats(); +} + +static int display_ports_get_height(void) +{ + return stats_get_n_ports(); +} + +static struct display_screen display_screen_ports = { + .draw_frame = display_ports_draw_frame, + .draw_stats = display_ports_draw_stats, + .get_height = display_ports_get_height, + .title = "ports", +}; + +struct display_screen *display_ports(void) +{ + return &display_screen_ports; +} diff --git a/VNFs/DPPD-PROX/display_ports.h b/VNFs/DPPD-PROX/display_ports.h new file mode 100644 index 00000000..520662fb --- /dev/null +++ b/VNFs/DPPD-PROX/display_ports.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_PORTS_H +#define DISPLAY_PORTS_H + +struct display_screen; +struct display_screen *display_ports(void); + +#endif /* DISPLAY_PORTS_H */ diff --git a/VNFs/DPPD-PROX/display_priority.c b/VNFs/DPPD-PROX/display_priority.c new file mode 100644 index 00000000..c997d85d --- /dev/null +++ b/VNFs/DPPD-PROX/display_priority.c @@ -0,0 +1,144 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "display_priority.h" +#include "stats_prio_task.h" +#include "display.h" +#include "lconf.h" + +#define PRIORITY_COUNT 8 + +static struct display_page display_page_priority; +static struct display_column *stats_tx[PRIORITY_COUNT]; +static struct display_column *stats_drop[PRIORITY_COUNT]; +static struct display_column *core_col; +static struct display_column *name_col; + +static void display_priority_draw_frame(struct screen_state *state) +{ + uint32_t n_tasks = stats_get_n_prio_tasks_tot(); + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + char name[32]; + char *ptr; + + display_page_init(&display_page_priority); + + struct display_table *core_name = display_page_add_table(&display_page_priority); + + display_table_init(core_name, "Core/task"); + core_col = display_table_add_col(core_name); + name_col = display_table_add_col(core_name); + display_column_init(core_col, "Nb", 4); + display_column_init(name_col, "Name", 5); + + struct display_table *stats = display_page_add_table(&display_page_priority); + if (state->toggle == 0) { + display_table_init(stats, "Statistics per second"); + + char title[64]; + for (int i = 0; i < PRIORITY_COUNT; ++i) { + stats_tx[i] = display_table_add_col(stats); + snprintf(title, sizeof(title), "TX %d (K)", i); + display_column_init(stats_tx[i], title, 9); + + stats_drop[i] = display_table_add_col(stats); + snprintf(title, sizeof(title), "DRP %d (K)", i); + display_column_init(stats_drop[i], title, 9); + } + } else { + display_table_init(stats, "Total statistics"); + + char title[64]; + for (int i = 0; i < PRIORITY_COUNT; ++i) { + stats_tx[i] = display_table_add_col(stats); + snprintf(title, sizeof(title), "TX %d (#)", i); + display_column_init(stats_tx[i], title, 9); + + stats_drop[i] = display_table_add_col(stats); + snprintf(title, sizeof(title), "DRP %d (#)", i); + display_column_init(stats_drop[i], title, 9); + } + } + + display_page_draw_frame(&display_page_priority, n_tasks); + + uint32_t count = 0; + lconf = NULL; + while (core_targ_next(&lconf, &targ, 0) == 0) { + if (strcmp(targ->task_init->mode_str, "aggreg") == 0) { + display_column_print_core_task(core_col, count, lconf, targ); + if (targ->id == 0) + display_column_print(name_col, count, "%s", lconf->name); + count++; + } + } +} + +static void display_priority_draw_stats(struct screen_state *state) +{ + uint64_t rx_prio; + uint64_t drop_tx_fail_prio; + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + const uint32_t n_stats_prio = stats_get_n_prio_tasks_tot(); + + if (state->toggle == 0) { + for (uint32_t count = 0; count < n_stats_prio; ++count) { + struct prio_task_stats_sample *last = stats_get_prio_task_stats_sample(count, 1); + struct prio_task_stats_sample *prev = stats_get_prio_task_stats_sample(count, 0); + + uint64_t delta_t = (last->tsc - prev->tsc) * 1000; + if (delta_t == 0) // This could happen if we just reset the screen => stats will be updated later + continue; + + for (uint8_t i = 0; i < PRIORITY_COUNT; i++) { + rx_prio = last->rx_prio[i] - prev->rx_prio[i]; + drop_tx_fail_prio = last->drop_tx_fail_prio[i] - prev->drop_tx_fail_prio[i]; + + display_column_print(stats_tx[i], count, "%9lu", val_to_rate(rx_prio, delta_t)); + display_column_print(stats_drop[i], count, "%9lu", val_to_rate(drop_tx_fail_prio, delta_t)); + } + } + } else { + for (uint32_t count = 0; count < n_stats_prio; ++count) { + for (uint8_t i = 0; i < PRIORITY_COUNT; i++) { + rx_prio = stats_core_task_tot_rx_prio(count, i); + drop_tx_fail_prio = stats_core_task_tot_drop_tx_fail_prio(count, i); + + display_column_print(stats_tx[i], count, "%9lu", rx_prio); + display_column_print(stats_drop[i], count, "%9lu", drop_tx_fail_prio); + } + } + } +} + +static int display_priority_get_height(void) +{ + return stats_get_n_prio_tasks_tot(); +} + +static struct display_screen display_screen_priority = { + .draw_frame = display_priority_draw_frame, + .draw_stats = display_priority_draw_stats, + .get_height = display_priority_get_height, + .title = "priority", +}; + +struct display_screen *display_priority(void) +{ + return &display_screen_priority; +} diff --git a/VNFs/DPPD-PROX/display_priority.h b/VNFs/DPPD-PROX/display_priority.h new file mode 100644 index 00000000..a15c03e6 --- /dev/null +++ b/VNFs/DPPD-PROX/display_priority.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_PRIORITY_H +#define DISPLAY_PRIORITY_H + +struct display_screen; +struct display_screen *display_priority(void); + +#endif /* DISPLAY_PRIORITY_H */ diff --git a/VNFs/DPPD-PROX/display_rings.c b/VNFs/DPPD-PROX/display_rings.c new file mode 100644 index 00000000..618350e2 --- /dev/null +++ b/VNFs/DPPD-PROX/display_rings.c @@ -0,0 +1,111 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ring.h> + +#include "display.h" +#include "display_rings.h" +#include "stats_ring.h" +#include "prox_port_cfg.h" + +static struct display_page display_page_rings; +static struct display_column *ring_col; +static struct display_column *occup_col; +static struct display_column *free_col; +static struct display_column *size_col; +static struct display_column *sc_col; +static struct display_column *sp_col; + +static void display_rings_draw_frame(struct screen_state *state) +{ + const uint32_t n_rings = stats_get_n_rings(); + char sc_val, sp_val; + + display_page_init(&display_page_rings); + + struct display_table *ring_table = display_page_add_table(&display_page_rings); + struct display_table *stats_table = display_page_add_table(&display_page_rings); + + display_table_init(ring_table, "Name"); + + display_table_init(stats_table, "Sampled statistics"); + + ring_col = display_table_add_col(ring_table); + display_column_init(ring_col, "Ring/Port", 11); + occup_col = display_table_add_col(stats_table); + display_column_init(occup_col, "Occup (%)", 11); + free_col = display_table_add_col(stats_table); + display_column_init(free_col, "Free", 11); + size_col = display_table_add_col(stats_table); + display_column_init(size_col, "Size", 11); + sc_col = display_table_add_col(stats_table); + display_column_init(sc_col, "SC", 2); + sp_col = display_table_add_col(stats_table); + display_column_init(sp_col, "SP", 2); + + display_page_draw_frame(&display_page_rings, n_rings); + + for (uint16_t i = 0; i < n_rings; ++i) { + struct ring_stats *rs = stats_get_ring_stats(i); + + if (rs->nb_ports == 0) { + display_column_print(ring_col, i, "%s", rs->ring->name); + } else { + char name[64] = {0}; + int offset = 0; + + for (uint32_t j = 0; j < rs->nb_ports; j++) + offset += sprintf(name + offset, "%s", rs->port[j]->name); + } + + sc_val = (rs->ring->flags & RING_F_SC_DEQ) ? 'y' : 'n'; + sp_val = (rs->ring->flags & RING_F_SP_ENQ) ? 'y' : 'n'; + + display_column_print(sc_col, i, " %c", sc_val); + display_column_print(sp_col, i, " %c", sp_val); + } +} + +static void display_rings_draw_stats(struct screen_state *state) +{ + const uint32_t n_rings = stats_get_n_rings(); + + for (uint32_t i = 0; i < n_rings; ++i) { + struct ring_stats *rs = stats_get_ring_stats(i); + uint32_t used = ((rs->size - rs->free)*10000)/rs->size; + + display_column_print(occup_col, i, "%8u.%02u", used/100, used%100); + display_column_print(free_col, i, "%11u", rs->free); + display_column_print(size_col, i, "%11u", rs->size); + } +} + +static int display_rings_get_height(void) +{ + return stats_get_n_rings(); +} + +static struct display_screen display_screen_rings = { + .draw_frame = display_rings_draw_frame, + .draw_stats = display_rings_draw_stats, + .get_height = display_rings_get_height, + .title = "rings", +}; + +struct display_screen *display_rings(void) +{ + return &display_screen_rings; +} diff --git a/VNFs/DPPD-PROX/display_rings.h b/VNFs/DPPD-PROX/display_rings.h new file mode 100644 index 00000000..421bb16e --- /dev/null +++ b/VNFs/DPPD-PROX/display_rings.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_RINGS_H +#define DISPLAY_RINGS_H + +struct display_screen; +struct display_screen *display_rings(void); + +#endif /* DISPLAY_RINGS_H */ diff --git a/VNFs/DPPD-PROX/display_tasks.c b/VNFs/DPPD-PROX/display_tasks.c new file mode 100644 index 00000000..75075a10 --- /dev/null +++ b/VNFs/DPPD-PROX/display_tasks.c @@ -0,0 +1,331 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "display_tasks.h" +#include "display.h" +#include "prox_globals.h" +#include "stats_task.h" +#include "stats_core.h" +#include "lconf.h" + +struct task_stats_disp { + uint32_t lcore_id; + uint32_t task_id; + uint32_t lcore_stat_id; +}; + +static int col_offset; +static struct task_stats_disp task_stats_disp[RTE_MAX_LCORE * MAX_TASKS_PER_CORE]; + +static struct display_page display_page_tasks; + +static struct display_column *nb_col; +static struct display_column *name_col; +static struct display_column *mode_col; +static struct display_column *rx_name_col; +static struct display_column *tx_name_col; +static struct display_column *idle_col; +static struct display_column *rx_col; +static struct display_column *tx_col; +static struct display_column *tx_fail_col; +static struct display_column *discard_col; +static struct display_column *handled_col; +static struct display_column *cpp_col; +static struct display_column *ghz_col; +static struct display_column *rx_col; +static struct display_column *tx_col; +static struct display_column *tx_fail_col; +static struct display_column *discard_col; +static struct display_column *handled_col; +static struct display_column *occup_col; +static struct display_column *mask_col; +static struct display_column *class_col; +static struct display_column *mbm_tot_col; +static struct display_column *mbm_loc_col; +static struct display_column *frac_col; + +static void stats_display_core_task_entry(struct lcore_cfg *lconf, struct task_args *targ, unsigned row) +{ + display_column_print_core_task(nb_col, row, lconf, targ); + + display_column_print(name_col, row, "%s", targ->id == 0 ? lconf->name : ""); + display_column_print(mode_col, row, "%s", targ->task_init->mode_str); + + display_column_port_ring(rx_name_col, row, targ->rx_port_queue, targ->nb_rxports, targ->rx_rings, targ->nb_rxrings); + display_column_port_ring(tx_name_col, row, targ->tx_port_queue, targ->nb_txports, targ->tx_rings, targ->nb_txrings); +} + +static void display_tasks_draw_frame(struct screen_state *state) +{ + const uint32_t n_tasks_tot = stats_get_n_tasks_tot(); + + display_page_init(&display_page_tasks); + + struct display_table *core_task = display_page_add_table(&display_page_tasks); + struct display_table *rx_tx = display_page_add_table(&display_page_tasks); + + display_table_init(core_task, "Core/Task"); + + nb_col = display_table_add_col(core_task); + display_column_init(nb_col, "Nb", 4); + name_col = display_table_add_col(core_task); + display_column_init(name_col, "Name", 7); + mode_col = display_table_add_col(core_task); + display_column_init(mode_col, "Mode", 9); + + display_table_init(rx_tx, "Port ID/Ring Name"); + rx_name_col = display_table_add_col(rx_tx); + display_column_init(rx_name_col, "RX", 9); + tx_name_col = display_table_add_col(rx_tx); + display_column_init(tx_name_col, "TX", 9); + + struct display_table *stats = display_page_add_table(&display_page_tasks); + + if (state->toggle == 0) { + display_table_init(stats, "Statistics per second"); + + idle_col = display_table_add_col(stats); + display_column_init(idle_col, "Idle (%)", 5); + + rx_col = display_table_add_col(stats); + display_column_init(rx_col, "RX (K)", 9); + + tx_col = display_table_add_col(stats); + display_column_init(tx_col, "TX (K)", 9); + + tx_fail_col = display_table_add_col(stats); + display_column_init(tx_fail_col, "TX Fail (K)", 9); + + discard_col = display_table_add_col(stats); + display_column_init(discard_col, "Discard (K)", 9); + + handled_col = display_table_add_col(stats); + display_column_init(handled_col, "Handled (K)", 9); + + if (stats_cpu_freq_enabled()) { + struct display_table *other = display_page_add_table(&display_page_tasks); + + display_table_init(other, "Other"); + + cpp_col = display_table_add_col(other); + display_column_init(cpp_col, "CPP", 9); + + ghz_col = display_table_add_col(other); + display_column_init(ghz_col, "Clk (GHz)", 9); + } + if (stats_mbm_enabled()) { + struct display_table *other = display_page_add_table(&display_page_tasks); + mbm_tot_col = display_table_add_col(other); + display_column_init(mbm_tot_col, "Tot Bdw(M)", 10); + mbm_loc_col = display_table_add_col(other); + display_column_init(mbm_loc_col, "Loc Bdw(M)", 10); + } + } else { + display_table_init(stats, "Total Statistics"); + + rx_col = display_table_add_col(stats); + display_column_init(rx_col, "RX (K)", 14); + + tx_col = display_table_add_col(stats); + display_column_init(tx_col, "TX (K)", 14); + + tx_fail_col = display_table_add_col(stats); + display_column_init(tx_fail_col, "TX Fail (K)", 14); + + discard_col = display_table_add_col(stats); + display_column_init(discard_col, "Discard (K)", 14); + + handled_col = display_table_add_col(stats); + display_column_init(handled_col, "Handled (K)", 14); + + if (stats_cmt_enabled()) { + struct display_table *other = display_page_add_table(&display_page_tasks); + + display_table_init(other, "Cache QoS Monitoring"); + + occup_col = display_table_add_col(other); + display_column_init(occup_col, "Occupancy (KB)", 15); + + frac_col = display_table_add_col(other); + display_column_init(frac_col, "Fraction", 9); + } + if (stats_cat_enabled()) { + struct display_table *other = display_page_add_table(&display_page_tasks); + mask_col = display_table_add_col(other); + display_column_init(mask_col, "Cache mask", 10); + class_col = display_table_add_col(other); + display_column_init(class_col, "Class", 5); + } + } + display_page_draw_frame(&display_page_tasks, n_tasks_tot); + + uint16_t element_count = 0; + + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + PROX_ASSERT(element_count < RTE_MAX_LCORE * MAX_TASKS_PER_CORE); + + stats_display_core_task_entry(lconf, targ, element_count); + + task_stats_disp[element_count].lcore_id = lconf->id; + task_stats_disp[element_count].task_id = targ->id; + task_stats_disp[element_count].lcore_stat_id = stats_lcore_find_stat_id(lconf->id); + element_count++; + } +} + +static void print_kpps(struct display_column *col, int row, uint64_t nb_pkts, uint64_t delta_t) +{ + nb_pkts *= tsc_hz; + if (nb_pkts && nb_pkts /100 < delta_t) { + uint64_t int_part = nb_pkts/delta_t; + uint64_t frac_part = (nb_pkts - int_part * delta_t) * 1000 /delta_t; + display_column_print(col, row, "%2lu.%03lu", int_part, frac_part); + } + else { + display_column_print(col, row, "%9lu", nb_pkts / delta_t); + } +} + +static void display_core_task_stats_per_sec(const struct task_stats_disp *t, struct screen_state *state, int row) +{ + struct task_stats_sample *last = stats_get_task_stats_sample(t->lcore_id, t->task_id, 1); + struct task_stats_sample *prev = stats_get_task_stats_sample(t->lcore_id, t->task_id, 0); + + /* delta_t in units of clock ticks */ + uint64_t delta_t = last->tsc - prev->tsc; + + uint64_t empty_cycles = last->empty_cycles - prev->empty_cycles; + + if (empty_cycles > delta_t) { + empty_cycles = 10000; + } + else { + empty_cycles = empty_cycles * 10000 / delta_t; + } + + /* empty_cycles has 2 digits after point, (usefull when only a very small idle time) */ + + display_column_print(idle_col, row, "%3lu.%02lu", empty_cycles / 100, empty_cycles % 100); + + // Display per second statistics in Kpps unit + delta_t *= state->pps_unit; + + print_kpps(rx_col, row, last->rx_pkt_count - prev->rx_pkt_count, delta_t); + print_kpps(tx_col, row, last->tx_pkt_count - prev->tx_pkt_count, delta_t); + print_kpps(tx_fail_col, row, last->drop_tx_fail - prev->drop_tx_fail, delta_t); + print_kpps(discard_col, row, last->drop_discard - prev->drop_discard, delta_t); + print_kpps(handled_col, row, last->drop_handled - prev->drop_handled, delta_t); + + if (stats_cpu_freq_enabled()) { + uint8_t lcore_stat_id = t->lcore_stat_id; + struct lcore_stats_sample *clast = stats_get_lcore_stats_sample(lcore_stat_id, 1); + struct lcore_stats_sample *cprev = stats_get_lcore_stats_sample(lcore_stat_id, 0); + + uint64_t adiff = clast->afreq - cprev->afreq; + uint64_t mdiff = clast->mfreq - cprev->mfreq; + + uint64_t cpp = 0; + + uint64_t pkt_diff_rx = last->rx_pkt_count - prev->rx_pkt_count; + uint64_t pkt_diff_tx = last->tx_pkt_count - prev->tx_pkt_count; + + uint64_t pkt_diff = pkt_diff_tx > pkt_diff_rx? pkt_diff_tx : pkt_diff_rx; + + if (pkt_diff && mdiff) { + cpp = delta_t/pkt_diff*adiff/mdiff/1000; + } + + uint64_t mhz; + if (mdiff) + mhz = tsc_hz*adiff/mdiff/1000000; + else + mhz = 0; + + display_column_print(cpp_col, row, "%lu", cpp); + display_column_print(ghz_col, row, "%lu.%03lu", mhz/1000, mhz%1000); + } + if (stats_mbm_enabled()) { + struct lcore_stats *c = stats_get_lcore_stats(t->lcore_stat_id); + uint8_t lcore_stat_id = t->lcore_stat_id; + struct lcore_stats_sample *clast = stats_get_lcore_stats_sample(lcore_stat_id, 1); + struct lcore_stats_sample *cprev = stats_get_lcore_stats_sample(lcore_stat_id, 0); + if ((clast->mbm_tot_bytes - cprev->mbm_tot_bytes) >> 20) + display_column_print(mbm_tot_col, row, "%lu", (clast->mbm_tot_bytes - cprev->mbm_tot_bytes) >> 20); + else + display_column_print(mbm_tot_col, row, "0.%03lu", (clast->mbm_tot_bytes - cprev->mbm_tot_bytes) >> 10); + if( (clast->mbm_loc_bytes - cprev->mbm_loc_bytes) >> 20) + display_column_print(mbm_loc_col, row, "%lu", (clast->mbm_loc_bytes - cprev->mbm_loc_bytes) >> 20); + else + display_column_print(mbm_loc_col, row, "0.%03lu", (clast->mbm_loc_bytes - cprev->mbm_loc_bytes) >> 10); + } +} + +static void display_core_task_stats_tot(const struct task_stats_disp *t, struct screen_state *state, int row) +{ + struct task_stats *ts = stats_get_task_stats(t->lcore_id, t->task_id); + + display_column_print(rx_col, row, "%lu", ts->tot_rx_pkt_count); + display_column_print(tx_col, row, "%lu", ts->tot_tx_pkt_count); + display_column_print(tx_fail_col, row, "%lu", ts->tot_drop_tx_fail); + display_column_print(discard_col, row, "%lu", ts->tot_drop_discard); + display_column_print(handled_col, row, "%lu", ts->tot_drop_handled); + + if (stats_cmt_enabled()) { + struct lcore_stats *c = stats_get_lcore_stats(t->lcore_stat_id); + display_column_print(occup_col, row, "%lu", c->cmt_bytes >> 10); + display_column_print(frac_col, row, "%3lu.%02lu", c->cmt_fraction/100, c->cmt_fraction%100); + } + if (stats_cat_enabled()) { + struct lcore_stats *c = stats_get_lcore_stats(t->lcore_stat_id); + display_column_print(mask_col, row, "%x", c->cat_mask); + display_column_print(class_col, row, "%x", c->class); + } +} + +static void display_tasks_draw_stats(struct screen_state *state) +{ + const uint32_t n_tasks_tot = stats_get_n_tasks_tot(); + + for (uint8_t i = 0; i < n_tasks_tot; ++i) { + const struct task_stats_disp *disp = &task_stats_disp[i]; + + if (state->toggle == 0) { + display_core_task_stats_per_sec(disp, state, i); + } else { + display_core_task_stats_tot(disp, state, i); + } + } +} + +static int display_tasks_get_height(void) +{ + return stats_get_n_tasks_tot(); +} + +static struct display_screen display_screen_tasks = { + .draw_frame = display_tasks_draw_frame, + .draw_stats = display_tasks_draw_stats, + .get_height = display_tasks_get_height, + .title = "tasks", +}; + +struct display_screen *display_tasks(void) +{ + return &display_screen_tasks; +} diff --git a/VNFs/DPPD-PROX/display_tasks.h b/VNFs/DPPD-PROX/display_tasks.h new file mode 100644 index 00000000..b369b6bd --- /dev/null +++ b/VNFs/DPPD-PROX/display_tasks.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_TASKS_H +#define DISPLAY_TASKS_H + +struct display_screen; +struct display_screen *display_tasks(void); + +#endif /* DISPLAY_TASKS_H */ diff --git a/VNFs/DPPD-PROX/dpi/Makefile b/VNFs/DPPD-PROX/dpi/Makefile new file mode 100644 index 00000000..fc943580 --- /dev/null +++ b/VNFs/DPPD-PROX/dpi/Makefile @@ -0,0 +1,18 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +all: + gcc -fpic -shared dpi_stub.c -o dpi_stub.so diff --git a/VNFs/DPPD-PROX/dpi/dpi.h b/VNFs/DPPD-PROX/dpi/dpi.h new file mode 100644 index 00000000..5ce1015d --- /dev/null +++ b/VNFs/DPPD-PROX/dpi/dpi.h @@ -0,0 +1,76 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _DPI_H_ +#define _DPI_H_ + +#include <sys/time.h> +#include <inttypes.h> +#include <stddef.h> + +struct flow_info { + uint32_t ip_src; + uint32_t ip_dst; + uint8_t ip_proto; + uint16_t port_src; + uint16_t port_dst; + uint8_t reservered[3]; +} __attribute__((packed)); + +struct dpi_payload { + uint8_t *payload; + uint16_t len; + uint16_t client_to_server; + struct timeval tv; +}; + +struct dpi_engine { + /* Returns 0 on success, This function is called from an + arbitrary thread before any other function in this struct + is called. */ + int (*dpi_init)(uint32_t thread_count, int argc, const char *argv[]); + /* Return the size that should be allocated in the flow + table. It is the sizeof(*flow_data) passed to + dpi_process(). */ + size_t (*dpi_get_flow_entry_size)(void); + /* Called before the flow entry is expired. */ + void (*dpi_flow_expire)(void *flow_data); + /* start function called from a DPI thread itself. The opaque + pointer returned here will be passed to dpi_thread_stop and + dpi_process. */ + void *(*dpi_thread_start)(void); + /* Stop function called from a DPI thread itself. */ + void (*dpi_thread_stop)(void *opaque); + /* Processing function to perform actual DPI work. struct + flow_info contains the 5 tuple, flow_data is the entry in + the flow table which has a size specified by + dpi_get_flow_entry_size(). The payload (together with the + time and the direction) is passed through the payload + parameter. DPI results are returned by the results + array. The function returns 0 on success. */ + int (*dpi_process)(void *opaque, struct flow_info *fi, void *flow_data, + struct dpi_payload *payload, uint32_t results[], + size_t *result_len); + /* Called once at cleanup. */ + void (*dpi_finish)(void); + /* Function used for printing. */ + int (*dpi_print)(const char *fmt, ...); +}; + +/* Returns the implementation of a dpi_engine. */ +struct dpi_engine *get_dpi_engine(void); + +#endif /* _DPI_H_ */ diff --git a/VNFs/DPPD-PROX/dpi/dpi_stub.c b/VNFs/DPPD-PROX/dpi/dpi_stub.c new file mode 100644 index 00000000..8febbcb9 --- /dev/null +++ b/VNFs/DPPD-PROX/dpi/dpi_stub.c @@ -0,0 +1,57 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdio.h> + +#include "dpi.h" + +/* The following functions are not a real implementation of a + DPI. They serve only to create dpi_stub.so which can be loaded into + prox. */ + +static int dpi_init(uint32_t thread_count, int argc, const char *argv[]) +{ + return 0; +} + +size_t dpi_get_flow_entry_size(void) {return 0;} +void flow_data_dpi_flow_expire(void *flow_data) {} +void *dpi_thread_start() {return NULL;} +void dpi_thread_stop(void *opaque) {} +void dpi_finish(void) {} + +int dpi_process(void *opaque, struct flow_info *fi, void *flow_data, + struct dpi_payload *payload, uint32_t results[], + size_t *result_len) +{ + return 0; +} + +static struct dpi_engine dpi_engine = { + .dpi_init = dpi_init, + .dpi_get_flow_entry_size = dpi_get_flow_entry_size, + .dpi_flow_expire = flow_data_dpi_flow_expire, + .dpi_thread_start = dpi_thread_start, + .dpi_thread_stop = dpi_thread_stop, + .dpi_process = dpi_process, + .dpi_finish = dpi_finish, + .dpi_print = printf, +}; + +struct dpi_engine *get_dpi_engine(void) +{ + return &dpi_engine; +} diff --git a/VNFs/DPPD-PROX/eld.h b/VNFs/DPPD-PROX/eld.h new file mode 100644 index 00000000..b5de59d7 --- /dev/null +++ b/VNFs/DPPD-PROX/eld.h @@ -0,0 +1,82 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _ELD_H_ +#define _ELD_H_ + +#define PACKET_QUEUE_BITS 14 +#define PACKET_QUEUE_SIZE (1 << PACKET_QUEUE_BITS) +#define PACKET_QUEUE_MASK (PACKET_QUEUE_SIZE - 1) + +#define QUEUE_ID_BITS (32 - PACKET_QUEUE_BITS) +#define QUEUE_ID_SIZE (1 << QUEUE_ID_BITS) +#define QUEUE_ID_MASK (QUEUE_ID_SIZE - 1) + +struct early_loss_detect { + uint32_t entries[PACKET_QUEUE_SIZE]; + uint32_t last_pkt_idx; +}; + +static void early_loss_detect_reset(struct early_loss_detect *eld) +{ + for (size_t i = 0; i < PACKET_QUEUE_SIZE; i++) { + eld->entries[i] = -1; + } +} + +static uint32_t early_loss_detect_count_remaining_loss(struct early_loss_detect *eld) +{ + uint32_t queue_id; + uint32_t n_loss; + uint32_t n_loss_total = 0; + + /* Need to check if we lost any packet before last packet + received Any packet lost AFTER the last packet received + cannot be counted. Such a packet will be counted after both + lat and gen restarted */ + queue_id = eld->last_pkt_idx >> PACKET_QUEUE_BITS; + for (uint32_t i = (eld->last_pkt_idx + 1) & PACKET_QUEUE_MASK; i < PACKET_QUEUE_SIZE; i++) { + // We ** might ** have received OOO packets; do not count them as lost next time... + if (queue_id - eld->entries[i] != 0) { + n_loss = (queue_id - eld->entries[i] - 1) & QUEUE_ID_MASK; + n_loss_total += n_loss; + } + } + for (uint32_t i = 0; i < (eld->last_pkt_idx & PACKET_QUEUE_MASK); i++) { + // We ** might ** have received OOO packets; do not count them as lost next time... + if (eld->entries[i] - queue_id != 1) { + n_loss = (queue_id - eld->entries[i]) & QUEUE_ID_MASK; + n_loss_total += n_loss; + } + } + + eld->entries[eld->last_pkt_idx & PACKET_QUEUE_MASK] = -1; + return n_loss_total; +} + +static uint32_t early_loss_detect_add(struct early_loss_detect *eld, uint32_t packet_index) +{ + uint32_t old_queue_id, queue_pos, n_loss; + + eld->last_pkt_idx = packet_index; + queue_pos = packet_index & PACKET_QUEUE_MASK; + old_queue_id = eld->entries[queue_pos]; + eld->entries[queue_pos] = packet_index >> PACKET_QUEUE_BITS; + + return (eld->entries[queue_pos] - old_queue_id - 1) & QUEUE_ID_MASK; +} + +#endif /* _ELD_H_ */ diff --git a/VNFs/DPPD-PROX/etypes.h b/VNFs/DPPD-PROX/etypes.h new file mode 100644 index 00000000..97ce5c0d --- /dev/null +++ b/VNFs/DPPD-PROX/etypes.h @@ -0,0 +1,30 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _ETYPES_H_ +#define _ETYPES_H_ + +#define ETYPE_IPv4 0x0008 /* IPv4 in little endian */ +#define ETYPE_IPv6 0xDD86 /* IPv6 in little endian */ +#define ETYPE_ARP 0x0608 /* ARP in little endian */ +#define ETYPE_VLAN 0x0081 /* 802-1aq - VLAN */ +#define ETYPE_MPLSU 0x4788 /* MPLS unicast */ +#define ETYPE_MPLSM 0x4888 /* MPLS multicast */ +#define ETYPE_8021ad 0xA888 /* Q-in-Q */ +#define ETYPE_LLDP 0xCC88 /* Link Layer Discovery Protocol (LLDP) */ +#define ETYPE_EoGRE 0x5865 /* EoGRE in little endian */ + +#endif /* _ETYPES_H_ */ diff --git a/VNFs/DPPD-PROX/expire_cpe.c b/VNFs/DPPD-PROX/expire_cpe.c new file mode 100644 index 00000000..4f2f5cd8 --- /dev/null +++ b/VNFs/DPPD-PROX/expire_cpe.c @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "hash_entry_types.h" +#include "hash_utils.h" +#include "expire_cpe.h" + +#define MAX_TSC __UINT64_C(0xFFFFFFFFFFFFFFFF) + +void check_expire_cpe(void* data) +{ + struct expire_cpe *um = (struct expire_cpe *)data; + uint64_t cur_tsc = rte_rdtsc(); + struct cpe_data *entries[4] = {0}; + void *key[4] = {0}; + uint64_t n_buckets = get_bucket_key8(um->cpe_table, um->bucket_index, key, (void**)entries); + + for (uint8_t i = 0; i < 4 && entries[i]; ++i) { + if (entries[i]->tsc < cur_tsc) { + int key_found = 0; + void* entry = 0; + rte_table_hash_key8_ext_dosig_ops.f_delete(um->cpe_table, key[i], &key_found, entry); + } + } + + um->bucket_index++; + um->bucket_index &= (n_buckets - 1); +} diff --git a/VNFs/DPPD-PROX/expire_cpe.h b/VNFs/DPPD-PROX/expire_cpe.h new file mode 100644 index 00000000..ad697f76 --- /dev/null +++ b/VNFs/DPPD-PROX/expire_cpe.h @@ -0,0 +1,30 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _EXPIRE_CPE_H_ +#define _EXPIRE_CPE_H_ + +#include <rte_table_hash.h> + +struct expire_cpe { + struct rte_table_hash *cpe_table; + struct cpe_data *cpe_data; + uint32_t bucket_index; +}; + +void check_expire_cpe(void *data); + +#endif /* _EXPIRE_CPE_H_ */ diff --git a/VNFs/DPPD-PROX/file_utils.c b/VNFs/DPPD-PROX/file_utils.c new file mode 100644 index 00000000..b3cf0846 --- /dev/null +++ b/VNFs/DPPD-PROX/file_utils.c @@ -0,0 +1,92 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdio.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "prox_args.h" +#include "file_utils.h" + +static char file_error_string[128] = {0}; + +const char *file_get_error(void) +{ + return file_error_string; +} + +__attribute__((format(printf, 1 ,2))) static void file_set_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(file_error_string, sizeof(file_error_string), fmt, ap); + va_end(ap); +} + +static void resolve_path_cfg_dir(char *file_name, size_t len, const char *path) +{ + if (path[0] != '/') + snprintf(file_name, len, "%s/%s", get_cfg_dir(), path); + else + strncpy(file_name, path, len); +} + +long file_get_size(const char *path) +{ + char file_name[PATH_MAX]; + struct stat s; + + resolve_path_cfg_dir(file_name, sizeof(file_name), path); + + if (stat(file_name, &s)) { + file_set_error("Stat failed on '%s': %s", path, strerror(errno)); + return -1; + } + + if ((s.st_mode & S_IFMT) != S_IFREG) { + snprintf(file_error_string, sizeof(file_error_string), "'%s' is not a file", path); + return -1; + } + + return s.st_size; +} + +int file_read_content(const char *path, uint8_t *mem, size_t beg, size_t len) +{ + char file_name[PATH_MAX]; + FILE *f; + + resolve_path_cfg_dir(file_name, sizeof(file_name), path); + f = fopen(file_name, "r"); + if (!f) { + file_set_error("Failed to read '%s': %s", path, strerror(errno)); + return -1; + } + + fseek(f, beg, SEEK_SET); + + size_t ret = fread(mem, 1, len, f); + if ((uint32_t)ret != len) { + file_set_error("Failed to read '%s:%zu' for %zu bytes: got %zu\n", file_name, beg, len, ret); + return -1; + } + + fclose(f); + return 0; +} diff --git a/VNFs/DPPD-PROX/file_utils.h b/VNFs/DPPD-PROX/file_utils.h new file mode 100644 index 00000000..2458dff8 --- /dev/null +++ b/VNFs/DPPD-PROX/file_utils.h @@ -0,0 +1,27 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _FILE_UTILS_H_ +#define _FILE_UTILS_H_ + +#include <inttypes.h> +#include <stddef.h> + +long file_get_size(const char *path); +int file_read_content(const char *path, uint8_t *mem, size_t beg, size_t len); +const char *file_get_error(void); + +#endif /* _FILE_UTILS_H_ */ diff --git a/VNFs/DPPD-PROX/flow_gen/README b/VNFs/DPPD-PROX/flow_gen/README new file mode 100644 index 00000000..28f5d97c --- /dev/null +++ b/VNFs/DPPD-PROX/flow_gen/README @@ -0,0 +1,47 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +While it is possible to manually run stateful traffic generation as +described below, it is recommended to use the provided dpi scripts +available in the help-scripts directory.. + +Before running flow based generation, a traffic profile needs to be +extracted and copied into this directory. This is done by running the +flow extract tool. An example of running the tool is shown below. For +more details on the flow extract tool, please read the provided help +by running the tool with the -h argument. + +./build/flowextract2 -s 500000 -i input.pcap -o output_directory + +After the output has been copied to this directory, the configuration +can be launched as shown below: + +./build/prox -f flow_gen/flow_gen_4ports.cfg -e \ + -q max_setup_rate=2000 \ + -q connections=50000 \ + -q ss=19.46 \ + -q test_system_id=0 + +The parameters provided through -q depend on the traffic profile. The +following command can be used to find the maximum value of ss: + +./build/prox -f flow_gen/flow_gen_4ports.cfg -e \ + -q max_ss_and_quit=true \ + -q test_system_id=0 + +This will cause prox to read the traffic profile, calculate the maximum +value and quit immediately. No packets will be sent and the value for +ss will be printed on stdout. diff --git a/VNFs/DPPD-PROX/flow_gen/bundle_maker.lua b/VNFs/DPPD-PROX/flow_gen/bundle_maker.lua new file mode 100644 index 00000000..ca24d4bb --- /dev/null +++ b/VNFs/DPPD-PROX/flow_gen/bundle_maker.lua @@ -0,0 +1,94 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +function get_client_bundles(bundles) + local client_bundles = {}; + + for i,b in ipairs(bundles) do + client_bundles[i] = {bundle = b, imix_fraction = 1} + end + + return client_bundles; +end + +function get_server_streams(bundles) + local server_streams = {} + n_listen = 0 + for i, bundle in ipairs(bundles) do + for j, stream in ipairs(bundle) do + n_listen = n_listen + 1 + server_streams[n_listen] = stream + end + end + return server_streams; +end + +function setup_bundles(first_ip_byte, speed_scaling) + bundles = dofile("cfg.lua") + + local client_bundles = get_client_bundles(bundles); + local server_streams = get_server_streams(bundles); + + for i,e in ipairs(client_bundles) do + for j,stream in ipairs(e.bundle) do + stream.clients.ip[1] = first_ip_byte + stream.clients.port_mask = 0xffff + end + end + + for i,stream in ipairs(server_streams) do + stream.servers.ip[1] = first_ip_byte + end + + local highest_bps = 0; + for i,e in ipairs(client_bundles) do + for j,s in ipairs(e.bundle) do + if (s.up_bps ~= 1250000000 and s.dn_bps ~= 1250000000) then + if (highest_bps < s.up_bps) then + highest_bps = s.up_bps + end + if (highest_bps < s.dn_bps) then + highest_bps = s.dn_bps + end + end + end + end + + if (highest_bps == 0) then + highest_bps = 1250000000 + end + max_ss = 1250000000/highest_bps + + if (max_ss_and_quit == not nil and max_ss_and_quit == true) then + print("max ss=" .. max_ss .. "") + os.exit(0); + end + + if (speed_scaling > max_ss) then + error("Scaling too high (maximum scaling is " .. max_ss .. ")") + end + + for i,e in ipairs(client_bundles) do + for j,s in ipairs(e.bundle) do + if (s.up_bps ~= 1250000000 and s.dn_bps ~= 1250000000) then + s.up_bps = s.up_bps * speed_scaling; + s.dn_bps = s.dn_bps * speed_scaling; + end + end + end + + return client_bundles, server_streams +end diff --git a/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.cfg b/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.cfg new file mode 100644 index 00000000..ccac3eb7 --- /dev/null +++ b/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.cfg @@ -0,0 +1,150 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 2] +name=port_a +mac=00:00:00:00:00:03 +rx desc=512 +tx desc=1024 +[port 3] +name=port_b +mac=00:00:00:00:00:04 +rx desc=512 +tx desc=1024 + +[port 4] +name=port_c +mac=00:00:00:00:00:01 +rx desc=512 +tx desc=1024 +[port 5] +name=port_d +mac=00:00:00:00:00:02 +rx desc=512 +tx desc=1024 + +[lua] +dofile("flow_gen_4ports.lua") +[variables] +$drop=no + +[defaults] +mempool size=$mempool_size + +[global] +start time=5 +name=L4 Gen + +[core 0s0] +mode=master + +[core 1s0] +task=0 +mode=lbpos +tx cores=$port_a_clients +rx port=port_a +mempool size=32K +mbuf size=2560 +byte offset=26 +drop=$drop +ring size=16384 + +[core 1s0h] +task=0 +mode=lbpos +tx cores=$port_b_servers +rx port=port_b +mbuf size=2560 +byte offset=26 +drop=$drop +ring size=16384 + +;;;------------------------------ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +[core $port_a_clients] +name=p0 +task=0 +mode=genl4 +tx port=port_a +rx ring=yes +bps=$bps +streams=c_${self} +concur conn=$conn +max setup rate=$msr + +[core $port_b_servers] +name=p0 +task=0 +mode=genl4 +sub mode=server +rx ring=yes +tx port=port_b +bps=$bps +streams=s_${self} +concur conn=$conn + +;;;;;;; socket 1 ;;;;;;;;;;;;;;;;;;;;;;; + +[core 1s1] +name=ld +task=0 +mode=lbpos +tx cores=$port_c_clients +rx port=port_c +mempool size=32K +mbuf size=2560 +byte offset=26 +drop=$drop +ring size=16384 + +[core 1s1h] +name=ld +task=0 +mode=lbpos +tx cores=$port_d_servers +rx port=port_d +mbuf size=2560 +byte offset=26 +drop=$drop +ring size=16384 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +[core $port_c_clients] +name=p0 +task=0 +mode=genl4 +tx port=port_c +rx ring=yes +bps=$bps +streams=c_${self} +concur conn=$conn +max setup rate=$msr + +[core $port_d_servers] +name=p0 +task=0 +mode=genl4 +sub mode=server +rx ring=yes +tx port=port_d +bps=$bps +streams=s_${self} +concur conn=$conn diff --git a/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.lua b/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.lua new file mode 100644 index 00000000..ed674ef6 --- /dev/null +++ b/VNFs/DPPD-PROX/flow_gen/flow_gen_4ports.lua @@ -0,0 +1,83 @@ +-- +-- Copyright (c) 2010-2017 Intel Corporation +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +dofile("bundle_maker.lua") + +if (test_system_id == nil) then + error("test_system_id not set") +end + +offset = 8 * test_system_id + +c_2s0, s_3s0 = setup_bundles(128 + offset, ss) +c_4s0, s_5s0 = setup_bundles(129 + offset, ss) +c_2s0h, s_3s0h = setup_bundles(130 + offset, ss) +c_4s0h, s_5s0h = setup_bundles(131 + offset, ss) + +c_6s0, s_7s0 = setup_bundles(132 + offset, ss) +c_8s0, s_9s0 = setup_bundles(133 + offset, ss) +c_6s0h, s_7s0h = setup_bundles(134 + offset, ss) +c_8s0h, s_9s0h = setup_bundles(135 + offset, ss) + +---------------- + +c_2s1, s_3s1 = setup_bundles(64 + offset, ss) +c_4s1, s_5s1 = setup_bundles(65 + offset, ss) +c_2s1h, s_3s1h = setup_bundles(66 + offset, ss) +c_4s1h, s_5s1h = setup_bundles(67 + offset, ss) + +c_6s1, s_7s1 = setup_bundles(68 + offset, ss) +c_8s1, s_9s1 = setup_bundles(69 + offset, ss) +c_6s1h, s_7s1h = setup_bundles(70 + offset, ss) +c_8s1h, s_9s1h = setup_bundles(71 + offset, ss) + +if (max_setup_rate == nil) then + error("max_setup_rate not set") +end + +if (connections == nil) then + error("connections not set") +end + +port_a_clients="2s0,4s0,2s0h,4s0h,6s0,8s0,6s0h,8s0h" +port_b_servers="3s0,5s0,3s0h,5s0h,7s0,9s0,7s0h,9s0h" + + +port_c_clients="2s1,4s1,2s1h,4s1h,6s1,8s1,6s1h,8s1h" +port_d_servers="3s1,5s1,3s1h,5s1h,7s1,9s1,7s1h,9s1h" + +all_clients = port_a_clients + .. "," .. port_c_clients + +all_servers = port_b_servers + .. "," .. port_d_servers + +all_workers = all_clients .. "," .. all_servers + +all_ld = "1s0,1s0h,1s1,1s1h" + +client_port_count = 2; + +bps = 1250000000/task_count(port_a_clients) +msr = max_setup_rate/client_port_count/task_count(port_a_clients) +conn = connections/client_port_count/task_count(port_a_clients) + +mempool_size = connections +if (mempool_size > 100000) then + mempool_size = 100000 +elseif (mempool_size < 2048) then + mempool_size = 2048 +end diff --git a/VNFs/DPPD-PROX/flow_iter.h b/VNFs/DPPD-PROX/flow_iter.h new file mode 100644 index 00000000..1ff5eeeb --- /dev/null +++ b/VNFs/DPPD-PROX/flow_iter.h @@ -0,0 +1,37 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _FLOW_ITER_H_ +#define _FLOW_ITER_H_ + +struct task_args; + +struct flow_iter { + /* Returns a new iterator pointing to the beginning of the collection. */ + void (*beg)(struct flow_iter *iter, struct task_args *targ); + /* Returns non-zero when parameter is pointing past the end of the collection. */ + int (*is_end)(struct flow_iter *iter, struct task_args *targ); + /* Moves iterator parameter forward by one. */ + void (*next)(struct flow_iter *iter, struct task_args *targ); + /* Access data. */ + uint16_t (*get_svlan)(struct flow_iter *iter, struct task_args *targ); + uint16_t (*get_cvlan)(struct flow_iter *iter, struct task_args *targ); + uint32_t (*get_gre_id)(struct flow_iter *iter, struct task_args *targ); + int idx; + uint8_t data; +}; + +#endif /* _FLOW_ITER_H_ */ diff --git a/VNFs/DPPD-PROX/fqueue.h b/VNFs/DPPD-PROX/fqueue.h new file mode 100644 index 00000000..ea38e85b --- /dev/null +++ b/VNFs/DPPD-PROX/fqueue.h @@ -0,0 +1,86 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _FQUEUE_H_ +#define _FQUEUE_H_ + +#include <rte_mbuf.h> + +#include <inttypes.h> + +struct fqueue { + uint32_t prod; + uint32_t cons; + uint32_t mask; + struct rte_mbuf *entries[0]; +}; + +static uint32_t fqueue_put(struct fqueue *q, struct rte_mbuf **mbufs, uint32_t count) +{ + uint32_t free_entries = q->mask + q->cons - q->prod; + uint32_t beg = q->prod & q->mask; + + count = count > free_entries? free_entries : count; + + if ((q->prod & q->mask) + count <= q->mask) { + rte_memcpy(&q->entries[q->prod & q->mask], mbufs, sizeof(mbufs[0]) * count); + q->prod += count; + } + else { + for (uint32_t i = 0; i < count; ++i) { + q->entries[q->prod & q->mask] = mbufs[i]; + q->prod++; + } + } + return count; +} + +static uint32_t fqueue_get(struct fqueue *q, struct rte_mbuf **mbufs, uint32_t count) +{ + uint32_t entries = q->prod - q->cons; + + count = count > entries? entries : count; + + if ((q->cons & q->mask) + count <= q->mask) { + rte_memcpy(mbufs, &q->entries[q->cons & q->mask], sizeof(mbufs[0]) * count); + q->cons += count; + } + else { + for (uint32_t i = 0; i < count; ++i) { + mbufs[i] = q->entries[q->cons & q->mask]; + q->cons++; + } + } + return count; +} + +static struct fqueue *fqueue_create(uint32_t size, int socket) +{ + size_t mem_size = 0; + + mem_size += sizeof(struct fqueue); + mem_size += sizeof(((struct fqueue *)(0))->entries[0]) * size; + + struct fqueue *ret = prox_zmalloc(mem_size, socket); + + if (!ret) + return NULL; + + ret->mask = size - 1; + return ret; +} + +#endif /* _FQUEUE_H_ */ diff --git a/VNFs/DPPD-PROX/gen/bng-4ports-gen.cfg b/VNFs/DPPD-PROX/gen/bng-4ports-gen.cfg new file mode 100644 index 00000000..ed0f0147 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/bng-4ports-gen.cfg @@ -0,0 +1,162 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:02 +[port 2] +name=cpe1 +mac=00:00:00:00:00:03 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=BNG gen +shuffle=yes +[core 0s0] +mode=master + +[core 1s0] +name=arp +task=0 +mode=gen +tx port=cpe0 + +bps=2138556 + +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 2s0] +name=cpe +task=0 +mode=gen +tx port=cpe0 +bps=1069289928 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 +lat pos=42 + +[core 3s0] +name=arp +task=0 +mode=gen +tx port=cpe1 + +bps=2138556 + +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 4s0] +name=cpe +task=0 +mode=gen +tx port=cpe1 +bps=1069289928 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 5s0] +name=inet0 +task=0 +mode=gen +tx port=inet0 +bps=1250000000; "1250000000./98" +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b + +random=0000000000000000XXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 6s0] +name=inet1 +task=0 +mode=gen +tx port=inet1 +bps=1250000000; "1250000000./98" +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=0000000000000000XXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 7s0] +name=CPE0 +task=0 +mode=lat +rx port=cpe0 +lat pos=42 + +[core 8s0] +name=CPE1 +task=0 +mode=lat +rx port=cpe1 +lat pos=42 + +[core 9s0] +name=INET0 +task=0 +mode=lat +rx port=inet0 +lat pos=66 + +[core 10s0] +name=INET1 +task=0 +mode=lat +rx port=inet1 +lat pos=66 diff --git a/VNFs/DPPD-PROX/gen/bng-8ports-gen-18cores.cfg b/VNFs/DPPD-PROX/gen/bng-8ports-gen-18cores.cfg new file mode 100644 index 00000000..7135648b --- /dev/null +++ b/VNFs/DPPD-PROX/gen/bng-8ports-gen-18cores.cfg @@ -0,0 +1,296 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=6 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:02 +[port 2] +name=cpe1 +mac=00:00:00:00:00:03 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 +[port 4] +name=cpe2 +mac=00:00:00:00:00:04 +[port 5] +name=inet2 +mac=00:00:00:00:00:04 +[port 6] +name=cpe3 +mac=00:00:00:00:00:04 +[port 7] +name=inet3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=BNG gen +shuffle=yes +[core 0s0] +mode=master + +[core 1s0] +name=arp +task=0 +mode=gen +;rx port=cpe0 +tx port=cpe0 + +bps=2138556 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +task=1 +mode=gen +;rx port=cpe1 +tx port=cpe1 + +bps=2138556 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000010XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +name=arp +task=2 +mode=gen +;rx port=cpe2 +tx port=cpe2 +bps=2138556 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +name=arp +task=3 +mode=gen +;rx port=cpe3 +tx port=cpe3 +bps=2138556 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000011XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 2s0] +name=cpe +task=0 +mode=gen +;rx port=cpe0 +tx port=cpe0 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 +lat pos=42 + +[core 3s0] +name=cpe +task=0 +mode=gen +;rx port=cpe1 +tx port=cpe1 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000010XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 4s0] +name=cpe +task=0 +mode=gen +;rx port=cpe2 +tx port=cpe2 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 5s0] +name=cpe +task=0 +mode=gen +;rx port=cpe3 +tx port=cpe3 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 +random=000000011XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 6s0] +name=inet0 +task=0 +mode=gen +;rx port=inet0 +tx port=inet0 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b + +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 7s0] +name=inet1 +task=0 +mode=gen +;rx port=inet1 +tx port=inet1 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 8s0] +name=inet2 +task=0 +mode=gen +;rx port=inet2 +tx port=inet2 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 9s0] +name=inet3 +task=0 +mode=gen +;rx port=inet3 +tx port=inet3 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 10s0] +name=CPE0 +task=0 +mode=lat +rx port=cpe0 +lat pos=42 + +[core 11s0] +name=CPE1 +task=0 +mode=lat +rx port=cpe1 +lat pos=42 + +[core 12s0] +name=CPE2 +task=0 +mode=lat +rx port=cpe2 +lat pos=42 + +[core 13s0] +name=CPE3 +task=0 +mode=lat +rx port=cpe3 +lat pos=42 + +[core 14s0] +name=INET0 +task=0 +mode=lat +rx port=inet0 +lat pos=66 + +[core 15s0] +name=INET1 +task=0 +mode=lat +rx port=inet1 +lat pos=66 + +[core 16s0] +name=INET2 +task=0 +mode=lat +rx port=inet2 +lat pos=66 + +[core 17s0] +name=INET3 +task=0 +mode=lat +rx port=inet3 +lat pos=66 diff --git a/VNFs/DPPD-PROX/gen/bng-8ports-gen.cfg b/VNFs/DPPD-PROX/gen/bng-8ports-gen.cfg new file mode 100644 index 00000000..a988f65c --- /dev/null +++ b/VNFs/DPPD-PROX/gen/bng-8ports-gen.cfg @@ -0,0 +1,300 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:02 +[port 2] +name=cpe1 +mac=00:00:00:00:00:03 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 +[port 4] +name=cpe2 +mac=00:00:00:00:00:04 +[port 5] +name=inet2 +mac=00:00:00:00:00:04 +[port 6] +name=cpe3 +mac=00:00:00:00:00:04 +[port 7] +name=inet3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=BNG gen +shuffle=yes +[core 0s0] +mode=master + +[core 1s0] +name=arp +task=0 +mode=gen +;rx port=cpe0 +tx port=cpe0 + +bps=2138556 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 1s0h] +name=cpe +task=0 +mode=gen +;rx port=cpe0 +tx port=cpe0 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000000XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 +lat pos=42 + +[core 2s0] +name=arp +task=0 +mode=gen +;rx port=cpe1 +tx port=cpe1 + +bps=2138556 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000010XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 2s0h] +name=cpe +task=0 +mode=gen +;rx port=cpe1 +tx port=cpe1 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 + +random=000000010XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 3s0] +name=arp +task=0 +mode=gen +;rx port=cpe2 +tx port=cpe2 +bps=2138556 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 3s0h] +name=cpe +task=0 +mode=gen +;rx port=cpe2 +tx port=cpe2 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 +random=000000001XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 4s0] +name=arp +task=0 +mode=gen +;rx port=cpe3 +tx port=cpe3 +bps=2138556 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 06 00 01 08 00 06 04 00 02 00 1e 67 3e b8 df c0 a8 01 01 00 00 00 00 00 00 c0 a8 01 01 +random=000000011XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +[core 4s0h] +name=cpe +task=0 +mode=gen +;rx port=cpe3 +tx port=cpe3 +bps=1069289928 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 21 +random=000000011XXXXXXX +rand_offset=14 + +random=0000XXXX00XX00XX +rand_offset=18 + +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +lat pos=42 + +[core 5s0] +name=inet0 +task=0 +mode=gen +;rx port=inet0 +tx port=inet0 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b + +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 6s0] +name=inet1 +task=0 +mode=gen +;rx port=inet1 +tx port=inet1 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 7s0] +name=inet2 +task=0 +mode=gen +;rx port=inet2 +tx port=inet2 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 0s0h] +name=inet3 +task=0 +mode=gen +;rx port=inet3 +tx port=inet3 +bps=1250000000; "1250000000./98" +pkt inline=ab cd ef 01 23 45 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 38 00 01 00 00 40 2f 7c 94 7f 00 00 01 7f 00 00 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 f6 b7 c0 a8 01 c7 c0 a8 01 01 00 35 00 35 00 08 7b 5b +random=000000000000000XXXXXXXXXXXXXXXXX +rand_offset=42 ; gre ID + +lat pos=66 + +[core 0s1] +name=CPE0 +task=0 +mode=lat +rx port=cpe0 +lat pos=42 + +[core 1s1] +name=CPE1 +task=0 +mode=lat +rx port=cpe1 +lat pos=42 + +[core 2s1] +name=CPE2 +task=0 +mode=lat +rx port=cpe2 +lat pos=42 + +[core 3s1] +name=CPE3 +task=0 +mode=lat +rx port=cpe3 +lat pos=42 + +[core 4s1] +name=INET0 +task=0 +mode=lat +rx port=inet0 +lat pos=66 + +[core 5s1] +name=INET1 +task=0 +mode=lat +rx port=inet1 +lat pos=66 + +[core 6s1] +name=INET2 +task=0 +mode=lat +rx port=inet2 +lat pos=66 + +[core 7s1] +name=INET3 +task=0 +mode=lat +rx port=inet3 +lat pos=66 diff --git a/VNFs/DPPD-PROX/gen/bng-ovs-usv-4ports-gen.cfg b/VNFs/DPPD-PROX/gen/bng-ovs-usv-4ports-gen.cfg new file mode 100644 index 00000000..13f4472c --- /dev/null +++ b/VNFs/DPPD-PROX/gen/bng-ovs-usv-4ports-gen.cfg @@ -0,0 +1,89 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +[port 1] +name=p1 +mac=00:00:00:00:00:02 +[port 2] +name=p2 +mac=00:00:00:00:00:03 +[port 3] +name=p3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=BNG OVS USV gen + +[core 0s1] +mode=master + +[core 1s1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 05 00 00 05 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b +random=0000000000000000000001XX00XX00XX +rand_offset=26 + +random=0000101X000000000XXX000000000000 +rand_offset=30 + +[core 2s1] +name=p1 +task=0 +mode=gen +tx port=p1 +bps=1250000000 +pkt inline=00 00 02 00 00 02 00 00 06 00 00 06 08 00 45 00 00 38 00 01 00 00 40 2f f7 43 c0 a8 01 01 c0 a8 01 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 88 f5 17 18 19 1a c0 a8 01 01 13 88 13 88 00 08 e6 f2 +random=000000000XXXXXXX +rand_offset=40 + +[core 3s1] +name=p2 +task=0 +mode=gen +tx port=p2 +bps=1250000000 +pkt inline=00 00 03 00 00 03 00 00 07 00 00 07 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b + +random=0000000000000000000000XX00XX00XX +rand_offset=26 + +random=0000101X000000000XXX000000000000 +rand_offset=30 + +[core 4s1] +name=p3 +task=0 +mode=gen +tx port=p3 +bps=1250000000 +pkt inline=00 00 04 00 00 04 00 00 08 00 00 08 08 00 45 00 00 38 00 01 00 00 40 2f f7 43 c0 a8 01 01 c0 a8 01 01 20 00 08 00 00 00 00 00 45 00 00 1c 00 01 00 00 40 11 88 f5 17 18 19 1a c0 a8 01 01 13 88 13 88 00 08 e6 f2 +random=000000000XXXXXXX +rand_offset=40 diff --git a/VNFs/DPPD-PROX/gen/l3fwd-gen.cfg b/VNFs/DPPD-PROX/gen/l3fwd-gen.cfg new file mode 100644 index 00000000..4d830043 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/l3fwd-gen.cfg @@ -0,0 +1,82 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +[port 1] +name=p1 +mac=00:00:00:00:00:02 +[port 2] +name=p2 +mac=00:00:00:00:00:03 +[port 3] +name=p3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Routing Gen + +[core 0s1] +mode=master + +[core 1s1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 0a 00 00 00 13 88 13 88 00 08 55 7b +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=30 + +[core 2s1] +name=p1 +task=0 +mode=gen +tx port=p1 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 0a 00 00 00 13 88 13 88 00 08 55 7b +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=30 + +[core 3s1] +name=p2 +task=0 +mode=gen +tx port=p2 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 0a 00 00 00 13 88 13 88 00 08 55 7b +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=30 + +[core 4s1] +name=p3 +task=0 +mode=gen +tx port=p3 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 0a 00 00 00 13 88 13 88 00 08 55 7b +random=0000101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=30 diff --git a/VNFs/DPPD-PROX/gen/lb_5tuple-gen.cfg b/VNFs/DPPD-PROX/gen/lb_5tuple-gen.cfg new file mode 100644 index 00000000..65d352a3 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/lb_5tuple-gen.cfg @@ -0,0 +1,82 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +[port 1] +name=p1 +mac=00:00:00:00:00:02 +[port 2] +name=p2 +mac=00:00:00:00:00:03 +[port 3] +name=p3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=5-tuple Gen + +[core 0s1] +mode=master + +[core 1s1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 47 00 f7 7d 00 00 00 00 00 00 00 00 00 00 00 00 77 23 55 7b +random=XXX00000 +rand_offset=23 +random=000000000000000000000000000XXXXX +rand_offset=26 +random=000000000000000000000000000XXXXX +rand_offset=30 +random=00000000000XXXXX00000000000XXXXX +rand_offset=34 + +[core 2s1] +name=p0 +task=0 +mode=nop +rx port=p0 + +[core 3s1] +name=p1 +task=0 +mode=nop +rx port=p1 + +[core 4s1] +name=p2 +task=0 +mode=nop +rx port=p2 + +[core 5s1] +name=p3 +task=0 +mode=nop +rx port=p3 diff --git a/VNFs/DPPD-PROX/gen/lw_aftr-gen.cfg b/VNFs/DPPD-PROX/gen/lw_aftr-gen.cfg new file mode 100644 index 00000000..a9aad397 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/lw_aftr-gen.cfg @@ -0,0 +1,106 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=inet_0 +mac=00:00:00:00:01:01 +[port 2] +name=lwB4_0 +mac=00:00:00:00:00:02 +[port 4] +name=inet_1 +mac=00:00:00:00:01:03 +[port 6] +name=lwB4_1 +mac=00:00:00:00:00:04 + +[variables] +$tun_pcap=./lwAFTR_tun_100k.pcap +$inet_pcap=./lwAFTR_inet_100k.pcap + +[defaults] +mempool size=16K + +[global] +start time=20 +name=Gen lwAFTR + +[core 0s0] +mode=master + +[core 1s0,2s0] +name=tun_0 +task=0 +mode=gen +tx port=lwB4_0 +pcap file=$tun_pcap +lat pos=58 + +[core 3s0,4s0] +name=inet_0 +task=0 +mode=gen +tx port=inet_0 +pcap file=$inet_pcap +lat pos=18 + +[core 1s1,2s1] +name=tun_1 +task=0 +mode=gen +tx port=lwB4_1 +pcap file=$tun_pcap +lat pos=58 + +[core 3s1,4s1] +name=inet_1 +task=0 +mode=gen +tx port=inet_1 +pcap file=$inet_pcap +lat pos=18 + +[core 5s0] +name=lat_in0 +task=0 +mode=lat +rx port=inet_0 +lat pos=18 + +[core 6s0] +name=lat_tun0 +task=0 +mode=lat +rx port=lwB4_0 +lat pos=58 + +[core 5s1] +name=lat_in1 +task=0 +mode=lat +rx port=inet_1 +lat pos=18 + +[core 6s1] +name=lat_tun1 +task=0 +mode=lat +rx port=lwB4_1 +lat pos=58 diff --git a/VNFs/DPPD-PROX/gen/nop-gen.cfg b/VNFs/DPPD-PROX/gen/nop-gen.cfg new file mode 100644 index 00000000..8c801034 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/nop-gen.cfg @@ -0,0 +1,71 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +[port 1] +name=p1 +mac=00:00:00:00:00:02 +[port 2] +name=p2 +mac=00:00:00:00:00:03 +[port 3] +name=p3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Basic Gen + +[core 0s0] +mode=master + +[core 1s0] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b +[core 2s0] +name=p1 +task=0 +mode=gen +tx port=p1 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b +[core 3s0] +name=p2 +task=0 +mode=gen +tx port=p2 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b +[core 4s0] +name=p3 +task=0 +mode=gen +tx port=p3 +bps=1250000000 +pkt inline=00 00 01 00 00 01 00 00 02 00 00 02 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d c0 a8 01 01 c0 a8 01 01 13 88 13 88 00 08 55 7b diff --git a/VNFs/DPPD-PROX/gen/nsh-gen.cfg b/VNFs/DPPD-PROX/gen/nsh-gen.cfg new file mode 100644 index 00000000..8502d0ef --- /dev/null +++ b/VNFs/DPPD-PROX/gen/nsh-gen.cfg @@ -0,0 +1,50 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +[port 1] +name=p1 +mac=00:00:00:00:00:02 +[port 2] +name=p2 +mac=00:00:00:00:00:03 +[port 3] +name=p3 +mac=00:00:00:00:00:04 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Basic Gen + +[core 0s1] +mode=master + +[core 1s1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=68 05 ca 30 6b d0 68 05 ca 30 6c b0 08 00 45 00 04 20 00 00 40 00 40 11 a5 fd c8 02 00 65 c8 02 00 66 9c c4 12 b6 04 0c 00 00 0c 40 00 04 00 00 00 00 40 06 01 03 00 03 e9 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 07 00 00 00 01 00 06 00 08 00 45 00 03 d6 00 00 00 00 40 06 48 15 0a 00 00 01 c0 a8 64 64 00 00 00 00 00 00 00 00 00 00 00 00 50 00 00 00 d9 b0 00 00 00 01 02 03 04 05 06 07 08 09 0a diff --git a/VNFs/DPPD-PROX/gen/pe-4ports-gen.cfg b/VNFs/DPPD-PROX/gen/pe-4ports-gen.cfg new file mode 100644 index 00000000..c7a01615 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/pe-4ports-gen.cfg @@ -0,0 +1,239 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:02 +[port 2] +name=cpe1 +mac=00:00:00:00:00:03 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 + + +[variables] +$up_size=60 +$dn_size=60 +[defaults] +mempool size=4K + +[global] +start time=5 +name=PE gen +[core 0s1] +mode=master + +[core 1s1] +name=cpe0 +task=0 +mode=gen +tx port=cpe0 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d +random=000000000000000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000000XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + +pkt size=$up_size +lat pos=50 + + +[core 2s1] +name=cpe1 +task=0 +mode=gen +tx port=cpe1 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000010000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000010XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + + +pkt size=$up_size +lat pos=50 + +[core 3s1] +name=cpe0 +task=0 +mode=gen +tx port=cpe0 +bps=625000000 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000001000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000001XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + + +pkt size=$up_size +lat pos=50 + +[core 4s1] +name=cpe1 +task=0 +mode=gen +tx port=cpe1 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000011000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000011XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + +pkt size=$up_size +lat pos=50 + + +[core 5s1] +name=inet0 +task=0 +mode=gen +tx port=inet0 +bps=1250000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + +pkt size=$dn_size +lat pos=46 + +[core 6s1] +name=inet1 +task=0 +mode=gen +tx port=inet1 +bps=1250000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + +pkt size=$dn_size +lat pos=46 + +[core 7s1] +name=none +task=0 +mode=lat +rx port=cpe0 +lat pos=50 + +[core 8s1] +name=none +task=0 +mode=lat +rx port=cpe1 +lat pos=50 + +[core 9s1] +name=none +task=0 +mode=lat +rx port=inet0 +lat pos=46 + +[core 10s1] +name=none +task=0 +mode=lat +rx port=inet1 +lat pos=46 diff --git a/VNFs/DPPD-PROX/gen/pe-8ports-gen.cfg b/VNFs/DPPD-PROX/gen/pe-8ports-gen.cfg new file mode 100644 index 00000000..461fd4b0 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/pe-8ports-gen.cfg @@ -0,0 +1,314 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=cpe0 +mac=00:00:00:00:00:01 +[port 1] +name=inet0 +mac=00:00:00:00:00:02 +[port 2] +name=cpe1 +mac=00:00:00:00:00:03 +[port 3] +name=inet1 +mac=00:00:00:00:00:04 + + +[port 4] +name=cpe2 +mac=00:00:00:00:00:01 +[port 5] +name=inet2 +mac=00:00:00:00:00:02 +[port 6] +name=cpe3 +mac=00:00:00:00:00:03 +[port 7] +name=inet3 +mac=00:00:00:00:00:04 + +[variables] +$up_size=60 +$dn_size=60 +[defaults] +mempool size=4K + +[global] +start time=5 +name=PE gen +[core 0s1] +mode=master + +[core 1s1,1s1h] +name=cpe0 +task=0 +mode=gen +tx port=cpe0 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d +random=000000000000000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000000XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + +pkt size=$up_size +lat pos=50 + + +[core 2s1,2s1h] +name=cpe1 +task=0 +mode=gen +tx port=cpe1 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000010000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000010XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + + +pkt size=$up_size +lat pos=50 + +[core 3s1,3s1h] +name=inet0 +task=0 +mode=gen +tx port=inet0 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + + +pkt size=$dn_size +lat pos=46 + +[core 4s1,4s1h] +name=inet1 +task=0 +mode=gen +tx port=inet1 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + +pkt size=$dn_size +lat pos=46 + +[core 5s1] +name=none +task=0 +mode=lat +rx port=cpe0 +lat pos=50 + +[core 5s1h] +name=none +task=0 +mode=lat +rx port=cpe1 +lat pos=50 + +[core 6s1] +name=none +task=0 +mode=lat +rx port=inet0 +lat pos=46 + +[core 6s1h] +name=none +task=0 +mode=lat +rx port=inet1 +lat pos=46 + +[core 1s0,1s0h] +name=cpe2 +task=0 +mode=gen +tx port=cpe2 +bps=625000000 + +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000001000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000001XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + + +pkt size=$up_size +lat pos=50 + +[core 2s0,2s0h] +name=cpe3 +task=0 +mode=gen +tx port=cpe3 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 a8 00 01 81 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 f7 79 c0 a8 01 01 c0 a8 01 01 00 35 00 35 00 08 7c 1d + +random=000000000011000X +rand_offset=14 + +random=00000000XXXXXXXX +rand_offset=18 + +random=11000000101010000011XXXXXXXXXXXX +rand_offset=34 + +random=0X00101XXXXXXXXXXXXX0000XXXXXXXX +rand_offset=38 + +random=XXXXXX00 +rand_offset=23 + +random=000000000000XXX0 +rand_offset=42 + +random=0000000XXXXXXXXX +rand_offset=44 + +pkt size=$up_size +lat pos=50 + +[core 3s0,3s0h] +name=inet2 +task=0 +mode=gen +tx port=inet2 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + +pkt size=$dn_size +lat pos=46 + +[core 4s0,4s0h] +name=inet3 +task=0 +mode=gen +tx port=inet3 +bps=625000000 +pkt inline=ff ff ff ff ff ff 00 00 00 00 00 00 88 47 00 00 31 00 45 00 00 20 00 01 00 00 40 11 77 fa c0 a8 80 80 c0 a8 01 01 00 35 00 35 00 08 fc 9d + +random=110000001010100000XXXXXXXXXXXXXX +rand_offset=34 + +random=XXXXXX00 +rand_offset=19 + +pkt size=$dn_size +lat pos=46 + +[core 5s0] +name=none +task=0 +mode=lat +rx port=cpe2 +lat pos=50 + +[core 5s0h] +name=none +task=0 +mode=lat +rx port=cpe3 +lat pos=50 + +[core 6s0] +name=none +task=0 +mode=lat +rx port=inet2 +lat pos=46 + +[core 6s0h] +name=none +task=0 +mode=lat +rx port=inet3 +lat pos=46 diff --git a/VNFs/DPPD-PROX/gen/vRouter-gen-4ports.cfg b/VNFs/DPPD-PROX/gen/vRouter-gen-4ports.cfg new file mode 100644 index 00000000..403ac7df --- /dev/null +++ b/VNFs/DPPD-PROX/gen/vRouter-gen-4ports.cfg @@ -0,0 +1,179 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +rx desc=$rxd +tx desc=$txd +[port 1] +name=p1 +mac=00:00:00:00:00:02 +rx desc=$rxd +tx desc=$txd +[port 2] +name=p2 +mac=00:00:00:00:00:03 +rx desc=$rxd +tx desc=$txd +[port 3] +name=p3 +mac=00:00:00:00:00:04 +rx desc=$rxd +tx desc=$txd + +[variables] +$bulk=8 +$rxd=1024 +$txd=256 +$c1=1s1,1s1h,9s1,9s1h +$c2=2s1,2s1h,10s1,10s1h +$c3=3s1,3s1h,11s1,11s1h +$c4=4s1,4s1h,12s1,12s1h +$r1=5s1 +$r2=6s1 +$r3=7s1 +$r4=8s1 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Basic Gen + +[core 0s1] +mode=master + +[core $c1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 38 00 00 01 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 00 00 11 00 00 02 42 00 00 02 13 88 13 88 00 0c 00 00 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c2] +name=p1 +task=0 +mode=gen +tx port=p1 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 39 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 19 00 00 02 43 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c3] +name=p2 +task=0 +mode=gen +tx port=p2 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 44 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 01 00 00 02 40 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c4] +name=p3 +task=0 +mode=gen +tx port=p3 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 45 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 09 00 00 02 41 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $r1] +name=r1 +task=0 +mode=arp +rx port=p0 +tx port=p0 +tx cores=(${r1})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r2] +name=r2 +task=0 +mode=arp +rx port=p1 +tx port=p1 +tx cores=(${r2})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r3] +name=r3 +task=0 +mode=arp +rx port=p2 +tx port=p2 +tx cores=(${r3})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r4] +name=r4 +task=0 +mode=arp +rx port=p3 +tx port=p3 +tx cores=(${r4})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 diff --git a/VNFs/DPPD-PROX/gen/vRouter-gen.cfg b/VNFs/DPPD-PROX/gen/vRouter-gen.cfg new file mode 100644 index 00000000..c02183c0 --- /dev/null +++ b/VNFs/DPPD-PROX/gen/vRouter-gen.cfg @@ -0,0 +1,323 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[port 0] +name=p0 +mac=00:00:00:00:00:01 +rx desc=$rxd +tx desc=$txd +[port 1] +name=p1 +mac=00:00:00:00:00:02 +rx desc=$rxd +tx desc=$txd +[port 2] +name=p2 +mac=00:00:00:00:00:03 +rx desc=$rxd +tx desc=$txd +[port 3] +name=p3 +mac=00:00:00:00:00:04 +rx desc=$rxd +tx desc=$txd +[port 4] +name=p4 +mac=00:00:00:00:00:05 +rx desc=$rxd +tx desc=$txd +[port 5] +name=p5 +mac=00:00:00:00:00:06 +rx desc=$rxd +tx desc=$txd +[port 6] +name=p6 +mac=00:00:00:00:00:07 +rx desc=$rxd +tx desc=$txd +[port 7] +name=p7 +mac=00:00:00:00:00:08 +rx desc=$rxd +tx desc=$txd + +[variables] +$bulk=8 +$rxd=1024 +$txd=256 +$c1=1s1,1s1h,9s1,9s1h +$c2=2s1,2s1h,10s1,10s1h +$c3=3s1,3s1h,11s1,11s1h +$c4=4s1,4s1h,12s1,12s1h +$c5=5s1,5s1h,13s1,13s1h +$c6=6s1,6s1h,14s1,14s1h +$c7=7s1,7s1h,15s1,15s1h +$c8=8s1,8s1h,16s1,16s1h +$r1=1s0 +$r2=2s0 +$r3=3s0 +$r4=4s0 +$r5=5s0 +$r6=6s0 +$r7=7s0 +$r8=10s0 + +[defaults] +mempool size=4K + +[global] +start time=5 +name=Basic Gen + +[core 0s1] +mode=master + +[core $c1] +name=p0 +task=0 +mode=gen +tx port=p0 +bps=1250000000 +pkt inline=00 1b 21 b1 23 14 00 00 01 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 00 00 21 00 00 02 44 00 00 02 13 88 13 88 00 0c 00 00 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c2] +name=p1 +task=0 +mode=gen +tx port=p1 +bps=1250000000 +pkt inline=00 1b 21 b1 23 15 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 29 00 00 02 45 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c3] +name=p2 +task=0 +mode=gen +tx port=p2 +bps=1250000000 +pkt inline=90 e2 ba a7 64 44 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 31 00 00 02 46 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c4] +name=p3 +task=0 +mode=gen +tx port=p3 +bps=1250000000 +pkt inline=90 e2 ba a7 64 45 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 39 00 00 02 47 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c5] +name=p4 +task=0 +mode=gen +tx port=p4 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 38 00 00 01 00 00 01 08 00 45 00 00 20 00 01 00 00 40 11 00 00 11 00 00 02 42 00 00 02 13 88 13 88 00 0c 00 00 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c6] +name=p5 +task=0 +mode=gen +tx port=p5 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 39 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 19 00 00 02 43 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c7] +name=p6 +task=0 +mode=gen +tx port=p6 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 44 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 01 00 00 02 40 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $c8] +name=p7 +task=0 +mode=gen +tx port=p7 +bps=1250000000 +pkt inline=90 e2 ba a5 a4 45 00 00 02 00 00 02 08 00 45 00 00 20 00 01 00 00 40 11 00 00 09 00 00 02 41 00 00 02 13 88 13 88 00 0c 55 7b 00 00 00 00 00 00 00 00 00 +min bulk size=$bulk +max bulk size=$bulk +random=0XXXXXXXXXXXXX10 +rand_offset=34 +random=0XXXXXXXXXXXXX10 +rand_offset=36 +lat pos=42 +packet id pos=46 + +[core $r1] +name=r1 +task=0 +mode=arp +rx port=p0 +tx port=p0 +tx cores=(${r1})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r2] +name=r2 +task=0 +mode=arp +rx port=p1 +tx port=p1 +tx cores=(${r2})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r3] +name=r3 +task=0 +mode=arp +rx port=p2 +tx port=p2 +tx cores=(${r3})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r4] +name=r4 +task=0 +mode=arp +rx port=p3 +tx port=p3 +tx cores=(${r4})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r5] +name=r5 +task=0 +mode=arp +rx port=p4 +tx port=p4 +tx cores=(${r5})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r6] +name=r6 +task=0 +mode=arp +rx port=p5 +tx port=p5 +tx cores=(${r6})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r7] +name=r7 +task=0 +mode=arp +rx port=p6 +tx port=p6 +tx cores=(${r7})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 + +[core $r8] +name=r8 +task=0 +mode=arp +rx port=p7 +tx port=p7 +tx cores=(${r8})t1 +task=1 +mode=lat +rx ring=yes +lat pos=42 +packet id pos=46 diff --git a/VNFs/DPPD-PROX/genl4_bundle.c b/VNFs/DPPD-PROX/genl4_bundle.c new file mode 100644 index 00000000..7d4a0141 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_bundle.c @@ -0,0 +1,369 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <rte_hash.h> +#include <rte_memory.h> +#include <rte_hash_crc.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "prox_assert.h" +#include "cdf.h" +#include "defines.h" +#include "genl4_bundle.h" +#include "log.h" +#include "pkt_parser.h" +#include "prox_lua_types.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#define RTE_CACHE_LINE_ROUNDUP CACHE_LINE_ROUNDUP +#endif + +/* zero on success */ +int bundle_ctx_pool_create(const char *name, uint32_t n_elems, struct bundle_ctx_pool *ret, uint32_t *occur, uint32_t n_occur, struct bundle_cfg *cfg, int socket_id) +{ + size_t memsize; + uint8_t *mem; + + const struct rte_hash_parameters params = { + .name = name, + .entries = rte_align32pow2(n_elems) * 8, + //.bucket_entries = 8, + .key_len = sizeof(struct pkt_tuple), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + .socket_id = socket_id, + }; + + ret->hash = rte_hash_create(¶ms); + if (NULL == ret->hash) + return -1; + + uint32_t rand_pool_size = 0, tot_occur = 0; + + if (occur) { + for (uint32_t i = 0; i < n_occur; ++i) { + tot_occur += occur[i]; + } + + rand_pool_size = (n_elems + (tot_occur - 1))/tot_occur*tot_occur; + } + + memsize = 0; + memsize += RTE_CACHE_LINE_ROUNDUP(params.entries * sizeof(ret->hash_entries[0])); + memsize += RTE_CACHE_LINE_ROUNDUP(n_elems * sizeof(ret->free_bundles[0])); + memsize += RTE_CACHE_LINE_ROUNDUP(n_elems * sizeof(ret->bundles[0])); + if (occur) + memsize += RTE_CACHE_LINE_ROUNDUP(rand_pool_size * sizeof(ret->occur)); + mem = prox_zmalloc(memsize, socket_id); + if (NULL == mem) + return -1; + + ret->hash_entries = (struct bundle_ctx **) mem; + mem += RTE_CACHE_LINE_ROUNDUP(params.entries * sizeof(ret->hash_entries[0])); + ret->free_bundles = (struct bundle_ctx **) mem; + mem += RTE_CACHE_LINE_ROUNDUP(n_elems * sizeof(ret->free_bundles[0])); + if (occur) { + ret->occur = (uint32_t *)mem; + mem += RTE_CACHE_LINE_ROUNDUP(rand_pool_size * sizeof(ret->occur)); + + ret->seed = rte_rdtsc(); + + size_t cur_occur = 0; + size_t j = 0; + + for (uint32_t i = 0; i < rand_pool_size; ++i) { + while (j >= occur[cur_occur]) { + cur_occur++; + if (cur_occur == n_occur) + cur_occur = 0; + j = 0; + } + j++; + ret->occur[i] = cur_occur; + } + ret->n_occur = rand_pool_size; + } + ret->bundles = (struct bundle_ctx *) mem; + + ret->bundle_cfg = cfg; + for (unsigned i = 0; i < n_elems; ++i) { + ret->free_bundles[i] = &ret->bundles[i]; + } + ret->n_free_bundles = n_elems; + ret->tot_bundles = n_elems; + + return 0; +} + +struct bundle_ctx *bundle_ctx_pool_get(struct bundle_ctx_pool *p) +{ + if (p->n_free_bundles > 0) + return p->free_bundles[--p->n_free_bundles]; + return NULL; +} + +static struct bundle_cfg *bundle_ctx_get_cfg(struct bundle_ctx_pool *p) +{ + uint32_t rand = 0; + + /* get rand in [0, RAND_MAX rounded down] */ + do { + rand = rand_r(&p->seed); + } while (rand >= RAND_MAX/p->n_occur*p->n_occur); + + rand /= RAND_MAX/p->n_occur; + + PROX_ASSERT(p->n_occur); + PROX_ASSERT(rand < p->n_occur); + + uint32_t r = p->occur[rand]; + p->occur[rand] = p->occur[--p->n_occur]; + + return &p->bundle_cfg[r]; +} + +static void bundle_ctx_put_cfg(struct bundle_ctx_pool *p, const struct bundle_cfg *cfg) +{ + if (p->occur) { + uint32_t r = cfg - p->bundle_cfg; + p->occur[p->n_occur++] = r; + } +} + +struct bundle_ctx *bundle_ctx_pool_get_w_cfg(struct bundle_ctx_pool *p) +{ + if (p->n_free_bundles > 0) { + struct bundle_ctx *ret = p->free_bundles[--p->n_free_bundles]; + ret->cfg = bundle_ctx_get_cfg(p); + return ret; + } + + return NULL; +} + +void bundle_ctx_pool_put(struct bundle_ctx_pool *p, struct bundle_ctx *bundle) +{ + bundle_ctx_put_cfg(p, bundle->cfg); + p->free_bundles[p->n_free_bundles++] = bundle; +} + +static void bundle_cleanup(struct bundle_ctx *bundle) +{ + if (bundle->heap_ref.elem != NULL) { + heap_del(bundle->heap, &bundle->heap_ref); + } +} + +static int bundle_iterate_streams(struct bundle_ctx *bundle, struct bundle_ctx_pool *pool, unsigned *seed, struct l4_stats *l4_stats) +{ + enum l4gen_peer peer; + int ret = 0, old; + + while (bundle->ctx.stream_cfg->is_ended(&bundle->ctx)) { + + if (bundle->ctx.stream_cfg->proto == IPPROTO_TCP) { + if (bundle->ctx.retransmits == 0) + l4_stats->tcp_finished_no_retransmit++; + else + l4_stats->tcp_finished_retransmit++; + } + else + l4_stats->udp_finished++; + + if (bundle->stream_idx + 1 != bundle->cfg->n_stream_cfgs) { + ret = 1; + bundle->stream_idx++; + + stream_ctx_reset_move(&bundle->ctx, bundle->cfg->stream_cfgs[bundle->stream_idx]); + + /* Update tuple */ + old = rte_hash_del_key(pool->hash, &bundle->tuple); + if (old < 0) { + plogx_err("Failed to delete key while trying to change tuple: %d (%s)\n",old, strerror(-old)); + } + plogx_dbg("Moving to stream with idx %d\n", bundle->stream_idx); + + /* In case there are multiple streams, clients + randomized but ports fixed, it is still + possible to hit an infinite loop here. The + situations is hit if a client:port is + connected to a server:port in one of the + streams while client:port is regenerated + for the first stream. There is no conflict + yet since the server:port is + different. Note that this is bug since a + client:port can only have one open + connection. */ + int retries = 0; + do { + bundle_create_tuple(&bundle->tuple, &bundle->cfg->clients, bundle->ctx.stream_cfg, 0, seed); + + ret = rte_hash_lookup(pool->hash, (const void *)&bundle->tuple); + if (++retries == 1000) { + plogx_warn("Already tried 1K times\n"); + plogx_warn("Going from %d to %d\n", bundle->stream_idx -1, bundle->stream_idx); + } + } while (ret >= 0); + + ret = rte_hash_add_key(pool->hash, &bundle->tuple); + if (ret < 0) { + plogx_err("Failed to add key while moving to next stream!\n"); + return -1; + } + pool->hash_entries[ret] = pool->hash_entries[old]; + + if (bundle->ctx.stream_cfg->proto == IPPROTO_TCP) + l4_stats->tcp_created++; + else + l4_stats->udp_created++; + } + else { + int a = rte_hash_del_key(pool->hash, &bundle->tuple); + PROX_PANIC(a < 0, "Del failed (%d)! during finished all bundle (%d)\n", a, bundle->cfg->n_stream_cfgs); + bundle_cleanup(bundle); + bundle_ctx_pool_put(pool, bundle); + + return -1; + } + } + return ret; +} + +void bundle_create_tuple(struct pkt_tuple *tp, const struct host_set *clients, const struct stream_cfg *stream_cfg, int rnd_ip, unsigned *seed) +{ + tp->dst_port = clients->port; + tp->dst_port &= ~clients->port_mask; + tp->dst_port |= rand_r(seed) & clients->port_mask; + + if (rnd_ip) { + tp->dst_addr = clients->ip; + tp->dst_addr &= ~clients->ip_mask; + tp->dst_addr |= rand_r(seed) & clients->ip_mask; + } + + tp->src_addr = stream_cfg->servers.ip; + tp->src_port = stream_cfg->servers.port; + plogx_dbg("bundle_create_tuple() with proto = %x, %d\n", stream_cfg->proto, rnd_ip); + tp->proto_id = stream_cfg->proto; + + tp->l2_types[0] = 0x0008; +} + +void bundle_init_w_cfg(struct bundle_ctx *bundle, const struct bundle_cfg *cfg, struct heap *heap, enum l4gen_peer peer, unsigned *seed) +{ + bundle->cfg = cfg; + bundle_init(bundle, heap, peer, seed); +} + +void bundle_init(struct bundle_ctx *bundle, struct heap *heap, enum l4gen_peer peer, unsigned *seed) +{ + bundle->heap_ref.elem = NULL; + bundle->heap = heap; + memset(&bundle->ctx, 0, sizeof(bundle->ctx)); + // TODO; assert that there is at least one stream + bundle->stream_idx = 0; + + stream_ctx_init(&bundle->ctx, peer, bundle->cfg->stream_cfgs[bundle->stream_idx], &bundle->tuple); + bundle_create_tuple(&bundle->tuple, &bundle->cfg->clients, bundle->ctx.stream_cfg, peer == PEER_CLIENT, seed); +} + +void bundle_expire(struct bundle_ctx *bundle, struct bundle_ctx_pool *pool, struct l4_stats *l4_stats) +{ + struct pkt_tuple *pt = &bundle->tuple; + + plogx_dbg("Client = "IPv4_BYTES_FMT":%d, Server = "IPv4_BYTES_FMT":%d\n", + IPv4_BYTES(((uint8_t*)&pt->dst_addr)), + rte_bswap16(pt->dst_port), + IPv4_BYTES(((uint8_t*)&pt->src_addr)), + rte_bswap16(pt->src_port)); + + int a = rte_hash_del_key(pool->hash, bundle); + if (a < 0) { + plogx_err("Del failed with error %d: '%s'\n", a, strerror(-a)); + plogx_err("ended = %d\n", bundle->ctx.flags & STREAM_CTX_F_TCP_ENDED); + } + + if (bundle->ctx.stream_cfg->proto == IPPROTO_TCP) + l4_stats->tcp_expired++; + else + l4_stats->udp_expired++; + + bundle_cleanup(bundle); + bundle_ctx_pool_put(pool, bundle); +} + +int bundle_proc_data(struct bundle_ctx *bundle, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, struct bundle_ctx_pool *pool, unsigned *seed, struct l4_stats *l4_stats) +{ + int ret; + uint64_t next_tsc; + + if (bundle->heap_ref.elem != NULL) { + heap_del(bundle->heap, &bundle->heap_ref); + } + + if (bundle_iterate_streams(bundle, pool, seed, l4_stats) < 0) + return -1; + + uint32_t retx_before = bundle->ctx.retransmits; + next_tsc = UINT64_MAX; + ret = bundle->ctx.stream_cfg->proc(&bundle->ctx, mbuf, l4_meta, &next_tsc); + + if (bundle->ctx.flags & STREAM_CTX_F_EXPIRED) { + bundle_expire(bundle, pool, l4_stats); + return -1; + } + else if (next_tsc != UINT64_MAX) { + heap_add(bundle->heap, &bundle->heap_ref, rte_rdtsc() + next_tsc); + } + l4_stats->tcp_retransmits += bundle->ctx.retransmits - retx_before; + + if (bundle_iterate_streams(bundle, pool, seed, l4_stats) > 0) { + if (bundle->heap_ref.elem != NULL) { + heap_del(bundle->heap, &bundle->heap_ref); + } + heap_add(bundle->heap, &bundle->heap_ref, rte_rdtsc()); + } + + return ret; +} + +uint32_t bundle_cfg_length(struct bundle_cfg *cfg) +{ + uint32_t ret = 0; + + for (uint32_t i = 0; i < cfg->n_stream_cfgs; ++i) { + ret += cfg->stream_cfgs[i]->n_bytes; + } + + return ret; +} + +uint32_t bundle_cfg_max_n_segments(struct bundle_cfg *cfg) +{ + uint32_t ret = 0; + uint32_t cur; + + for (uint32_t i = 0; i < cfg->n_stream_cfgs; ++i) { + cur = stream_cfg_max_n_segments(cfg->stream_cfgs[i]); + ret = ret > cur? ret: cur; + } + + return ret; +} diff --git a/VNFs/DPPD-PROX/genl4_bundle.h b/VNFs/DPPD-PROX/genl4_bundle.h new file mode 100644 index 00000000..94ceed91 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_bundle.h @@ -0,0 +1,89 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _GENL4_BUNDLE_H_ +#define _GENL4_BUNDLE_H_ + +#include "heap.h" +#include "genl4_stream.h" +#include "lconf.h" + +/* Configured once and used during packet generation. The structure + describes a single set of consecutive streams. When used at the + server side, it only contains a simple stream to represent a + service. */ +struct bundle_cfg { + struct host_set clients; + uint32_t n_stream_cfgs; + struct stream_cfg **stream_cfgs; +}; + +/* A bundle_ctx represents a an active stream between a client and a + server of servers. */ +struct bundle_ctx { + struct pkt_tuple tuple; /* Client IP/PORT generated once at bundle creation time, client PORT and server IP/PORT created when stream_idx++ */ + struct heap_ref heap_ref; /* Back reference into heap */ + struct heap *heap; /* timer management */ + + const struct bundle_cfg *cfg; /* configuration time read only structure */ + + struct stream_ctx ctx; /* state management info for stream_cfg (reset when stream_idx++) */ + uint32_t stream_idx; /* iterate through cfg->straem_cfgs */ +}; + +#define BUNDLE_CTX_UPCAST(r) ((struct bundle_ctx *)((uint8_t *)r - offsetof(struct bundle_ctx, heap_ref))) + +struct bundle_ctx_pool { + struct rte_hash *hash; + struct bundle_ctx **hash_entries; + struct bundle_ctx **free_bundles; + struct bundle_ctx *bundles; /* Memory containing all communications */ + uint32_t *occur; + struct bundle_cfg *bundle_cfg; + uint32_t n_occur; + uint32_t seed; + uint32_t n_free_bundles; + uint32_t tot_bundles; +}; + +struct l4_stats { + uint64_t bundles_created; + uint64_t tcp_finished_no_retransmit; + uint64_t tcp_finished_retransmit; + uint64_t udp_finished; + uint64_t tcp_created; + uint64_t udp_created; + uint64_t tcp_expired; + uint64_t tcp_retransmits; + uint64_t udp_expired; +}; + +struct cdf; +int bundle_ctx_pool_create(const char *name, uint32_t n_elems, struct bundle_ctx_pool *ret, uint32_t *occur, uint32_t n_occur, struct bundle_cfg *cfg, int socket_id); + +struct bundle_ctx *bundle_ctx_pool_get(struct bundle_ctx_pool *p); +struct bundle_ctx *bundle_ctx_pool_get_w_cfg(struct bundle_ctx_pool *p); +void bundle_ctx_pool_put(struct bundle_ctx_pool *p, struct bundle_ctx *bundle); + +void bundle_create_tuple(struct pkt_tuple *tp, const struct host_set *clients, const struct stream_cfg *stream_cfg, int rnd_ip, unsigned *seed); +void bundle_init(struct bundle_ctx *bundle, struct heap *heap, enum l4gen_peer peer, unsigned *seed); +void bundle_init_w_cfg(struct bundle_ctx *bundle, const struct bundle_cfg *cfg, struct heap *heap, enum l4gen_peer peer, unsigned *seed); +void bundle_expire(struct bundle_ctx *bundle, struct bundle_ctx_pool *pool, struct l4_stats *l4_stats); +int bundle_proc_data(struct bundle_ctx *bundle, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, struct bundle_ctx_pool *pool, unsigned *seed, struct l4_stats *l4_stats); +uint32_t bundle_cfg_length(struct bundle_cfg *cfg); +uint32_t bundle_cfg_max_n_segments(struct bundle_cfg *cfg); + +#endif /* _GENL4_BUNDLE_H_ */ diff --git a/VNFs/DPPD-PROX/genl4_stream.h b/VNFs/DPPD-PROX/genl4_stream.h new file mode 100644 index 00000000..b180765d --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_stream.h @@ -0,0 +1,201 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _GENL4_STREAM_H_ +#define _GENL4_STREAM_H_ + +#include "prox_lua_types.h" +#include "pkt_parser.h" +#include "token_time.h" +#include "quit.h" + +enum tcp_state { + CLOSED, + LISTEN, + SYN_SENT, + SYN_RECEIVED, + ESTABLISHED, + CLOSE_WAIT, + LAST_ACK, + FIN_WAIT, + TIME_WAIT +}; + +static const char *tcp_state_to_str(const enum tcp_state s) +{ + switch(s) { + case CLOSED: + return "CLOSED"; + case LISTEN: + return "LISTEN"; + case SYN_SENT: + return "SYN_SENT"; + case SYN_RECEIVED: + return "SYN_RECEIVED"; + case ESTABLISHED: + return "ESTABLISHED"; + case CLOSE_WAIT: + return "CLOSE_WAIT"; + case LAST_ACK: + return "LAST_ACK"; + case FIN_WAIT: + return "FIN_WAIT"; + case TIME_WAIT: + return "TIME_WAIT"; + default: + return "INVALID_STATE"; + } +} + +#define STREAM_CTX_F_EXPIRED 0x01 +#define STREAM_CTX_F_NEW_DATA 0x02 /* Set on recv to track first ACK of data */ +#define STREAM_CTX_F_TCP_ENDED 0x04 +#define STREAM_CTX_F_TCP_GOT_SYN 0x08 /* Set only once when syn has been received */ +#define STREAM_CTX_F_TCP_GOT_FIN 0x10 /* Set only once when fin has been received */ +#define STREAM_CTX_F_MORE_DATA 0x20 +#define STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS 0x40 + +/* Run-time structure to management state information associated with current stream_cfg. */ +struct stream_ctx { + enum l4gen_peer peer; + uint32_t cur_action; + uint32_t cur_pos[2]; + enum tcp_state tcp_state; + struct token_time token_time; + struct token_time token_time_other; + uint16_t flags; + uint16_t same_state; + uint32_t next_seq; + uint32_t ackd_seq; + uint32_t recv_seq; + uint32_t ackable_data_seq; + uint32_t seq_first_byte; /* seq number - seq_first_byte gives offset within content. */ + uint32_t other_seq_first_byte; /* seq number - seq_first_byte gives offset within content. */ + uint32_t other_mss; + uint64_t sched_tsc; + uint32_t retransmits; + const struct stream_cfg *stream_cfg; /* Current active steam_cfg */ + struct pkt_tuple *tuple; +}; + +struct host_set { + uint32_t ip; + uint32_t ip_mask; + uint16_t port; + uint16_t port_mask; +}; + +struct stream_cfg { + struct peer_data data[2]; + struct host_set servers; // Current implementation only allows mask == 0. (i.e. single server) + struct token_time_cfg tt_cfg[2]; // bytes per period rate + uint16_t proto; + uint64_t tsc_timeout; + uint64_t tsc_timeout_time_wait; + uint32_t n_actions; + uint32_t n_pkts; + uint32_t n_bytes; + int (*proc)(struct stream_ctx *meta, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, uint64_t *next_tsc); + int (*is_ended)(struct stream_ctx *meta); + struct peer_action actions[0]; +}; + +static void scale_for_jitter(uint64_t *to_scale) +{ + (*to_scale) *= 2; +} + +static void reset_token_times(struct stream_ctx *ctx) +{ + const uint64_t now = rte_rdtsc(); + const struct stream_cfg *cfg = ctx->stream_cfg; + enum l4gen_peer peer = ctx->peer; + + token_time_init(&ctx->token_time, &cfg->tt_cfg[peer]); + token_time_reset_full(&ctx->token_time, now); + + token_time_init(&ctx->token_time_other, &cfg->tt_cfg[!peer]); + scale_for_jitter(&ctx->token_time_other.cfg.bytes_max); + token_time_reset_full(&ctx->token_time_other, now); +} + +static void stream_ctx_init(struct stream_ctx *ctx, enum l4gen_peer peer, struct stream_cfg *cfg, struct pkt_tuple *tuple) +{ + ctx->stream_cfg = cfg; + ctx->peer = peer; + ctx->tuple = tuple; + + /* Server's initial state is different from client for + TCP. For now, don't use a specific init function for + TCP/UDP since there is not a lot of difference and to avoid + an additional function pointer. */ + ctx->tcp_state = PEER_CLIENT == peer? CLOSED : LISTEN; + ctx->other_mss = 536; /* default 536 as per RFC 879 */ + + reset_token_times(ctx); +} + +static void stream_ctx_reset_move(struct stream_ctx *ctx, struct stream_cfg *cfg) +{ + enum l4gen_peer peer = ctx->peer; + struct pkt_tuple *tuple = ctx->tuple; + + memset(ctx, 0, sizeof(*ctx)); + stream_ctx_init(ctx, peer, cfg, tuple); +} + +static int stream_cfg_calc_max_payload_len(struct stream_cfg *cfg, enum l4gen_peer peer) +{ + const uint32_t l4_hdr_len = cfg->proto == IPPROTO_UDP? + sizeof(struct udp_hdr) : sizeof(struct tcp_hdr); + + return ETHER_MAX_LEN - ETHER_CRC_LEN - cfg->data[peer].hdr_len - l4_hdr_len; +} + +static int stream_cfg_max_n_segments(struct stream_cfg *cfg) +{ + if (cfg->proto == IPPROTO_UDP) + return 1; + + uint32_t ret = 1; + uint32_t cur; + + const uint32_t mss = stream_cfg_calc_max_payload_len(cfg, PEER_CLIENT); + + for (uint32_t i = 0; i < cfg->n_actions; ++i) { + cur = (cfg->actions[i].len + (mss - 1)) / mss; + ret = ret > cur? ret: cur; + } + + return ret; +} + +static int stream_cfg_verify_action(struct stream_cfg *cfg, struct peer_action *action) +{ + if (cfg->proto == IPPROTO_TCP) + return 0; + + uint16_t max_payload_len = stream_cfg_calc_max_payload_len(cfg, action->peer); + + PROX_PANIC(action->len > max_payload_len, + "Action %zu has length %u while for the maximum action length for UDP connections is limited to %u\n", + action - cfg->actions, + action->len, + max_payload_len); + return 0; +} + +#endif /* _GENL4_STREAM_H_ */ diff --git a/VNFs/DPPD-PROX/genl4_stream_tcp.c b/VNFs/DPPD-PROX/genl4_stream_tcp.c new file mode 100644 index 00000000..d05455b7 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_stream_tcp.c @@ -0,0 +1,965 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_ether.h> +#include <rte_eth_ctrl.h> + +#include "log.h" +#include "genl4_stream_tcp.h" +#include "prox_assert.h" +#include "mbuf_utils.h" + +static uint64_t tcp_retx_timeout(const struct stream_ctx *ctx) +{ + uint64_t delay = token_time_tsc_until_full(&ctx->token_time_other); + + return delay + ctx->stream_cfg->tsc_timeout; +} + +static uint64_t tcp_resched_timeout(const struct stream_ctx *ctx) +{ + uint64_t delay = token_time_tsc_until_full(&ctx->token_time); + + return delay; +} + +static void tcp_retx_timeout_start(struct stream_ctx *ctx, uint64_t *next_tsc) +{ + uint64_t now = rte_rdtsc(); + + *next_tsc = tcp_retx_timeout(ctx); + ctx->sched_tsc = now + *next_tsc; +} + +static int tcp_retx_timeout_occured(const struct stream_ctx *ctx, uint64_t now) +{ + return ctx->sched_tsc < now; +} + +static void tcp_retx_timeout_resume(const struct stream_ctx *ctx, uint64_t now, uint64_t *next_tsc) +{ + *next_tsc = ctx->sched_tsc - now; +} + +static void tcp_set_retransmit(struct stream_ctx *ctx) +{ + ctx->retransmits++; +} + +struct tcp_option { + uint8_t kind; + uint8_t len; +} __attribute__((packed)); + +void stream_tcp_create_rst(struct rte_mbuf *mbuf, struct l4_meta *l4_meta, struct pkt_tuple *tuple) +{ + struct tcp_hdr *tcp = (struct tcp_hdr *)l4_meta->l4_hdr; + struct ipv4_hdr *ip = ((struct ipv4_hdr *)tcp) - 1; + + ip->src_addr = tuple->dst_addr; + ip->dst_addr = tuple->src_addr; + + tcp->dst_port = tuple->src_port; + tcp->src_port = tuple->dst_port; + + ip->total_length = rte_bswap16(sizeof(struct ipv4_hdr) + sizeof(struct tcp_hdr)); + tcp->tcp_flags = TCP_RST_FLAG; + tcp->data_off = ((sizeof(struct tcp_hdr) / 4) << 4); + rte_pktmbuf_pkt_len(mbuf) = l4_meta->payload - rte_pktmbuf_mtod(mbuf, uint8_t *); + rte_pktmbuf_data_len(mbuf) = l4_meta->payload - rte_pktmbuf_mtod(mbuf, uint8_t *); +} + +static void create_tcp_pkt(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint8_t tcp_flags, int data_beg, int data_len) +{ + uint8_t *pkt; + + const struct peer_action *act = &ctx->stream_cfg->actions[ctx->cur_action]; + const struct stream_cfg *stream_cfg = ctx->stream_cfg; + + pkt = rte_pktmbuf_mtod(mbuf, uint8_t *); + rte_memcpy(pkt, stream_cfg->data[act->peer].hdr, stream_cfg->data[act->peer].hdr_len); + + struct ipv4_hdr *l3_hdr = (struct ipv4_hdr*)&pkt[stream_cfg->data[act->peer].hdr_len - sizeof(struct ipv4_hdr)]; + struct tcp_hdr *l4_hdr = (struct tcp_hdr *)&pkt[stream_cfg->data[act->peer].hdr_len]; + + l3_hdr->src_addr = ctx->tuple->dst_addr; + l3_hdr->dst_addr = ctx->tuple->src_addr; + l3_hdr->next_proto_id = IPPROTO_TCP; + + l4_hdr->src_port = ctx->tuple->dst_port; + l4_hdr->dst_port = ctx->tuple->src_port; + + uint32_t tcp_len = sizeof(struct tcp_hdr); + uint32_t tcp_payload_len = 0; + uint32_t seq_len = 0; + struct tcp_option *tcp_op; + + if (tcp_flags & TCP_RST_FLAG) { + tcp_flags |= TCP_RST_FLAG; + seq_len = 1; + } + else if (tcp_flags & TCP_SYN_FLAG) { + tcp_flags |= TCP_SYN_FLAG; + /* Window scaling */ + + /* TODO: make options come from the stream. */ + tcp_op = (struct tcp_option *)(l4_hdr + 1); + + tcp_op->kind = 2; + tcp_op->len = 4; + *(uint16_t *)(tcp_op + 1) = rte_bswap16(1460); /* TODO: Save this in this_mss */ + + tcp_len += 4; + seq_len = 1; + + ctx->seq_first_byte = ctx->ackd_seq + 1; + } + else if (tcp_flags & TCP_FIN_FLAG) { + tcp_flags |= TCP_FIN_FLAG; + seq_len = 1; + } + + if (tcp_flags & TCP_ACK_FLAG) { + l4_hdr->recv_ack = rte_bswap32(ctx->recv_seq); + tcp_flags |= TCP_ACK_FLAG; + } + else + l4_hdr->recv_ack = 0; + + uint16_t l4_payload_offset = stream_cfg->data[act->peer].hdr_len + tcp_len; + + if (data_len) { + seq_len = data_len; + plogx_dbg("l4 payload offset = %d\n", l4_payload_offset); + rte_memcpy(pkt + l4_payload_offset, stream_cfg->data[act->peer].content + data_beg, data_len); + } + + l4_hdr->sent_seq = rte_bswap32(ctx->next_seq); + l4_hdr->tcp_flags = tcp_flags; /* SYN */ + l4_hdr->rx_win = rte_bswap16(0x3890); // TODO: make this come from stream (config) + //l4_hdr->cksum = ...; + l4_hdr->tcp_urp = 0; + l4_hdr->data_off = ((tcp_len / 4) << 4); /* Highest 4 bits are TCP header len in units of 32 bit words */ + + /* ctx->next_seq = ctx->ackd_seq + seq_len; */ + ctx->next_seq += seq_len; + + /* No payload after TCP header. */ + rte_pktmbuf_pkt_len(mbuf) = l4_payload_offset + data_len; + rte_pktmbuf_data_len(mbuf) = l4_payload_offset + data_len; + + l3_hdr->total_length = rte_bswap16(sizeof(struct ipv4_hdr) + tcp_len + data_len); + plogdx_dbg(mbuf, NULL); + + plogx_dbg("put tcp packet with flags: %s%s%s, (len = %d, seq = %d, ack =%d)\n", + tcp_flags & TCP_SYN_FLAG? "SYN ":"", + tcp_flags & TCP_ACK_FLAG? "ACK ":"", + tcp_flags & TCP_FIN_FLAG? "FIN ":"", + data_len, rte_bswap32(l4_hdr->sent_seq), rte_bswap32(l4_hdr->recv_ack)); +} + +/* Get the length of the reply associated for the next packet. Note + that the packet will come from the other peer. In case the next + packet belongs to the current peer (again), the reply length will + be that of an empty TCP packet (i.e. the ACK). */ +uint16_t stream_tcp_reply_len(struct stream_ctx *ctx) +{ + if (stream_tcp_is_ended(ctx)) + return 0; + else if (ctx->tcp_state != ESTABLISHED) { + if (ctx->tcp_state == SYN_SENT || ctx->tcp_state == LISTEN) { + /* First packet received is a SYN packet. In + the current implementation this packet + contains the TCP option field to set the + MSS. For this, add 4 bytes. */ + return ctx->stream_cfg->data[!ctx->peer].hdr_len + sizeof(struct tcp_hdr) + 4; + } + return ctx->stream_cfg->data[!ctx->peer].hdr_len + sizeof(struct tcp_hdr); + } + else if (ctx->stream_cfg->actions[ctx->cur_action].peer == ctx->peer) { + /* The reply _could_ (due to races, still possibly + receive an old ack) contain data. This means that + in some cases, the prediction of the reply size + will be an overestimate. */ + uint32_t data_beg = ctx->next_seq - ctx->seq_first_byte; + const struct peer_action *act = &ctx->stream_cfg->actions[ctx->cur_action]; + + uint32_t remaining_len = act->len - (data_beg - act->beg); + + if (remaining_len == 0) { + if (ctx->cur_action + 1 != ctx->stream_cfg->n_actions) { + if (ctx->stream_cfg->actions[ctx->cur_action + 1].peer == ctx->peer) + return ctx->stream_cfg->data[ctx->peer].hdr_len + sizeof(struct tcp_hdr); + else { + uint32_t seq_beg = ctx->recv_seq - ctx->other_seq_first_byte; + uint32_t end = ctx->stream_cfg->actions[ctx->cur_action + 1].beg + + ctx->stream_cfg->actions[ctx->cur_action + 1].len; + uint32_t remaining = end - seq_beg; + uint16_t data_len = remaining > 1460? 1460: remaining; + + return ctx->stream_cfg->data[!ctx->peer].hdr_len + sizeof(struct tcp_hdr) + data_len; + } + } + else { + return ctx->stream_cfg->data[ctx->peer].hdr_len + sizeof(struct tcp_hdr); + } + } + else { + return ctx->stream_cfg->data[ctx->peer].hdr_len + sizeof(struct tcp_hdr); + } + } + else if (ctx->stream_cfg->actions[ctx->cur_action].peer != ctx->peer) { + uint32_t seq_beg = ctx->recv_seq - ctx->other_seq_first_byte; + uint32_t end = ctx->stream_cfg->actions[ctx->cur_action].beg + + ctx->stream_cfg->actions[ctx->cur_action].len; + uint32_t remaining = end - seq_beg; + uint16_t data_len = remaining > 1460? 1460: remaining; + + return ctx->stream_cfg->data[!ctx->peer].hdr_len + sizeof(struct tcp_hdr) + data_len; + } + else + return ctx->stream_cfg->data[ctx->peer].hdr_len + sizeof(struct tcp_hdr); +} + +static void stream_tcp_proc_in_order_data(struct stream_ctx *ctx, struct l4_meta *l4_meta, int *progress_seq) +{ + plogx_dbg("Got data with seq %d (as expected), with len %d\n", ctx->recv_seq, l4_meta->len); + + if (!l4_meta->len) + return; + + const struct peer_action *act = &ctx->stream_cfg->actions[ctx->cur_action]; + enum l4gen_peer peer = act->peer; + /* Since we have received the expected sequence number, the start address will not exceed the cfg memory buffer. */ + uint8_t *content = ctx->stream_cfg->data[peer].content; + uint32_t seq_beg = ctx->recv_seq - ctx->other_seq_first_byte; + uint32_t end = ctx->stream_cfg->actions[ctx->cur_action].beg + ctx->stream_cfg->actions[ctx->cur_action].len; + uint32_t remaining = end - seq_beg; + + if (l4_meta->len > remaining) { + plogx_err("Provided data is too long:\n"); + plogx_err("action.beg = %d, action.len = %d", act->beg, act->len); + plogx_err("tcp seq points at %d in action, l4_meta->len = %d\n", seq_beg, l4_meta->len); + } + else { + if (memcmp(content + seq_beg, l4_meta->payload, l4_meta->len) == 0) { + plogx_dbg("Good payload in %d: %u -> %u\n", ctx->cur_action, ctx->recv_seq, l4_meta->len); + ctx->recv_seq += l4_meta->len; + ctx->cur_pos[peer] += l4_meta->len; + /* Move forward only when this was the last piece of data within current action (i.e. end of received data == end of action data). */ + if (seq_beg + l4_meta->len == act->beg + act->len) { + plogx_dbg("Got last piece in action %d\n", ctx->cur_action); + ctx->cur_action++; + } + else { + plogx_dbg("Got data from %d with len %d, but waiting for more (tot len = %d)!\n", seq_beg, l4_meta->len, act->len); + } + *progress_seq = 1; + ctx->flags |= STREAM_CTX_F_NEW_DATA; + } + else { + plogx_err("ackable = %d, ackd = %d\n", ctx->ackable_data_seq ,ctx->ackd_seq); + plogx_err("Bad payload action[%d]{.len = %d, .peer = %s}\n", ctx->cur_action, act->len, peer == PEER_SERVER? "s" : "c"); + plogx_err(" pkt payload len = %d, beginning at %u\n", l4_meta->len, seq_beg); + /* plogx_err(" Payload starts %zu bytes after beginning of l4_hdr\n", l4_meta->payload - l4_meta->l4_hdr); */ + + plogx_err(" payload[0-3] = %02x %02x %02x %02x\n", + l4_meta->payload[0], + l4_meta->payload[1], + l4_meta->payload[2], + l4_meta->payload[3]); + plogx_err(" expect[0-3] = %02x %02x %02x %02x\n", + content[seq_beg + 0], + content[seq_beg + 1], + content[seq_beg + 2], + content[seq_beg + 3]); + } + } +} + +static int stream_tcp_proc_in(struct stream_ctx *ctx, struct l4_meta *l4_meta) +{ + struct tcp_hdr *tcp = NULL; + int got_syn = 0; + int got_ack = 0; + int got_fin = 0; + int got_rst = 0; + + tcp = (struct tcp_hdr *)l4_meta->l4_hdr; + + got_syn = tcp->tcp_flags & TCP_SYN_FLAG; + got_ack = tcp->tcp_flags & TCP_ACK_FLAG; + got_fin = tcp->tcp_flags & TCP_FIN_FLAG; + got_rst = tcp->tcp_flags & TCP_RST_FLAG; + plogx_dbg("TCP, flags: %s%s%s, (len = %d, seq = %d, ack =%d)\n", got_syn? "SYN ":"", got_ack? "ACK ":"", got_fin? "FIN " : "", l4_meta->len, rte_bswap32(tcp->sent_seq), rte_bswap32(tcp->recv_ack)); + + if (got_syn) + ctx->flags |= STREAM_CTX_F_TCP_GOT_SYN; + if (got_fin) + ctx->flags |= STREAM_CTX_F_TCP_GOT_FIN; + + int progress_ack = 0, progress_seq = 0; + + /* RST => other side wants to terminate due to + inconsitent state (example: delay of retransmit of + last ACK while other side already closed the + connection. The other side will accept the packet + as a beginning of a new connection but there will + be no SYN. ) */ + if (got_rst) { + plogx_dbg("got rst\n"); + ctx->flags |= STREAM_CTX_F_TCP_ENDED; + return -1; + } + + if (got_ack) { + uint32_t ackd_seq = rte_bswap32(tcp->recv_ack); + + if (ackd_seq > ctx->ackd_seq) { + plogx_dbg("Got ACK for outstanding data, from %d to %d\n", ctx->ackd_seq, ackd_seq); + ctx->ackd_seq = ackd_seq; + plogx_dbg("ackable data = %d\n", ctx->ackable_data_seq); + /* Ackable_data_seq set to byte after + current action. */ + if (ctx->ackable_data_seq == ctx->ackd_seq) { + /* Due to retransmit in + combination with late acks, + is is possible to ack + future data. In this case, + the assumption that data + was lost is not true and + the next seq is moved + forward. */ + if (ctx->next_seq < ctx->ackable_data_seq) { + ctx->next_seq = ctx->ackable_data_seq; + } + + ctx->ackable_data_seq = 0; + const struct stream_cfg *stream_cfg = ctx->stream_cfg; + const struct peer_action *act = &stream_cfg->actions[ctx->cur_action]; + + ctx->cur_pos[act->peer] += act->len; + ctx->cur_action++; + plogx_dbg("Moving to next action %u\n", ctx->ackd_seq); + } + progress_ack = 1; + } + else { + plogx_dbg("Old data acked: acked = %d, ackable =%d\n", ackd_seq, ctx->ackd_seq); + } + } + + uint32_t seq = rte_bswap32(tcp->sent_seq); + + /* update recv_seq. */ + if (got_syn) { + /* When a syn is received, immediately reset recv_seq based on seq from packet. */ + ctx->recv_seq = seq + 1; + /* Syn packets have length 1, so the first real data will start after that. */ + ctx->other_seq_first_byte = seq + 1; + progress_seq = 1; + } + else if (got_fin) { + if (ctx->recv_seq == seq) { + plogx_dbg("Got fin with correct seq\n"); + ctx->recv_seq = seq + 1; + progress_seq = 1; + } + else { + plogx_dbg("Got fin but incorrect seq\n"); + } + } + else { + /* Only expect in-order packets. */ + if (ctx->recv_seq == seq) { + stream_tcp_proc_in_order_data(ctx, l4_meta, &progress_seq); + } + else if (ctx->recv_seq < seq) { + plogx_dbg("Future data received (got = %d, expected = %d), missing data! (data ignored)\n", seq, ctx->recv_seq); + } + else { + plogx_dbg("Old data received again (state = %s)\n", tcp_state_to_str(ctx->tcp_state)); + plogx_dbg("expecting seq %d, got seq %d, len = %d\n",ctx->recv_seq, seq, l4_meta->len); + plogx_dbg("ackd_seq = %d, next_seq = %d, action = %d\n", ctx->ackd_seq, ctx->next_seq, ctx->cur_action); + } + } + + /* parse options */ + if (((tcp->data_off >> 4)*4) > sizeof(struct tcp_hdr)) { + struct tcp_option *tcp_op = (struct tcp_option *)(tcp + 1); + uint8_t *payload = (uint8_t *)tcp + ((tcp->data_off >> 4)*4); + + do { + if (tcp_op->kind == 2 && tcp_op->len == 4) { + uint16_t mss = rte_bswap16(*(uint16_t *)(tcp_op + 1)); + ctx->other_mss = mss; + } + + tcp_op = (struct tcp_option *)(((uint8_t*)tcp_op) + tcp_op->len); + } while (((uint8_t*)tcp_op) < payload); + } + + if (progress_ack || progress_seq) { + ctx->same_state = 0; + ctx->flags |= STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS; + } + else { + ctx->flags &= ~STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS; + } + return 0; +} + +static int stream_tcp_proc_out_closed(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + /* create SYN packet in mbuf, return 0. goto SYN_SENT, set timeout */ + ctx->tcp_state = SYN_SENT; + + /* Initialize: */ + ctx->next_seq = 99; + ctx->ackd_seq = 99; + + create_tcp_pkt(ctx, mbuf, TCP_SYN_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; +} + +static int stream_tcp_proc_out_listen(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + if (!(ctx->flags & STREAM_CTX_F_TCP_GOT_SYN)) { + // TODO: keep connection around at end to catch retransmits from client + plogx_dbg("Got packet while listening without SYN (will send RST)\n"); + pkt_tuple_debug(ctx->tuple); + + ctx->flags |= STREAM_CTX_F_TCP_ENDED; + create_tcp_pkt(ctx, mbuf, TCP_RST_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } + + /* if syn received _now_, send ack + syn. goto SYN_RECEIVED. */ + plogx_dbg("Got packet while listen\n"); + + ctx->next_seq = 200; + ctx->ackd_seq = 200; + + ctx->tcp_state = SYN_RECEIVED; + + create_tcp_pkt(ctx, mbuf, TCP_SYN_FLAG | TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; +} + +static int stream_tcp_proc_out_syn_sent(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + if (ctx->ackd_seq < ctx->next_seq || !(ctx->flags & STREAM_CTX_F_TCP_GOT_SYN)) { + plogx_dbg("Retransmit SYN\n"); + /* Did not get packet, send syn again and keep state (waiting for ACK). */ + ++ctx->same_state; + tcp_set_retransmit(ctx); + return stream_tcp_proc_out_closed(ctx, mbuf, next_tsc); + } + + plogx_dbg("SYN_SENT and everything ACK'ed\n"); + plogx_dbg("ackd_seq = %d, next_seq = %d\n", ctx->ackd_seq, ctx->next_seq); + + /* If syn received for this stream, send ack and goto + ESTABLISHED. If first peer is this peer to send actual + data, schedule immediately. */ + + ctx->same_state = 0; + ctx->tcp_state = ESTABLISHED; + + /* third packet of three-way handshake will also contain + data. Don't send separate ACK yet. TODO: only send ACK if + data has not yet been ACK'ed. */ + if (ctx->stream_cfg->actions[ctx->cur_action].peer == ctx->peer) { + *next_tsc = tcp_resched_timeout(ctx); + plogx_dbg("immediately resched (%d)\n", ctx->cur_action); + return -1; + } + else { + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + } + return 0; +} + +static int stream_tcp_proc_out_syn_recv(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + if (ctx->ackd_seq == ctx->next_seq) { + /* Possible from server side with ctx->cur_action == 1 + if the current packet received had ACK for syn from + server to client and also data completing the first + action. */ + + ctx->same_state = 0; + ctx->tcp_state = ESTABLISHED; + if (ctx->stream_cfg->actions[ctx->cur_action].peer != ctx->peer) { + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } + else { + /* While at this point, an ACK without data + any could be sent by the server, it is not + really required because the next pacekt + after reschedule will also contain an ACK + along with new data. + + In this implementation, if this is the + case, the client is not only expecting an + ACK, but also actual data. For this reason, + the empty ACK packet should not be sent, + otherwise the client will retransmit its + data. + */ + + /* create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); */ + /* token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); */ + *next_tsc = tcp_resched_timeout(ctx); + return -1; + } + } + else { + /* Either this portion is executed due to a time-out + or due to packet reception, the SYN that has been + sent is not yet ACK'ed. So, retransmit the SYN/ACK. */ + plogx_dbg("Retransmit SYN/ACK\n"); + ++ctx->same_state; + tcp_set_retransmit(ctx); + ctx->next_seq = ctx->ackd_seq; + create_tcp_pkt(ctx, mbuf, TCP_SYN_FLAG | TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } +} + +static int stream_tcp_proc_out_estab_tx(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + const struct peer_action *act = &ctx->stream_cfg->actions[ctx->cur_action]; + + if (act->len == 0) { + plogx_dbg("Closing connection\n"); + /* This would be an ACK combined with FIN. To + send a separate ack. keep the state in + established, put_ack and expire + immediately*/ + plogx_dbg("Moving to FIN_WAIT\n"); + ctx->tcp_state = FIN_WAIT; + ctx->same_state = 0; + create_tcp_pkt(ctx, mbuf, TCP_FIN_FLAG | TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } + /* remaining_len2 will be zero, while in case of + act->len == 0, the connection can be closed + immediately. */ + + plogx_dbg("This peer to send!\n"); + uint32_t outstanding_bytes = ctx->next_seq - ctx->ackd_seq; + + uint32_t data_beg2 = ctx->next_seq - ctx->seq_first_byte; + uint32_t remaining_len2 = act->len - (data_beg2 - act->beg); + + const uint32_t rx_win = 300000; + /* If still data to be sent and allowed by outstanding amount */ + if (outstanding_bytes <= rx_win && remaining_len2) { + plogx_dbg("Outstanding bytes = %d, and remaining_len = %d, next_seq = %d\n", outstanding_bytes, remaining_len2, ctx->next_seq); + + if (ctx->ackable_data_seq == 0) { + PROX_ASSERT(outstanding_bytes == 0); + + ctx->ackable_data_seq = ctx->next_seq + act->len; + } + else + plogx_dbg("This will not be the first part of the data within an action\n"); + } + /* still data yet to be acked || still data to be sent but blocked by RX win. */ + else { + if (ctx->flags & STREAM_CTX_F_MORE_DATA) { + /* Don't send any packet. */ + ctx->flags &= ~STREAM_CTX_F_MORE_DATA; + *next_tsc = tcp_retx_timeout(ctx); + ctx->sched_tsc = rte_rdtsc() + *next_tsc; + return -1; + } + else { + uint64_t now = rte_rdtsc(); + + if ((ctx->flags & STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS) && token_time_tsc_until_full(&ctx->token_time_other) != 0) { + tcp_retx_timeout_start(ctx, next_tsc); + ctx->flags &= ~STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS; + return -1; + } + /* This function might be called due to packet + reception. In that case, cancel here and + wait until the timeout really occurs before + reTX. */ + if (!tcp_retx_timeout_occured(ctx, now)) { + tcp_retx_timeout_resume(ctx, now, next_tsc); + return -1; + } + + ctx->same_state++; + tcp_set_retransmit(ctx); + /* This possibly means that now retransmit is resumed half-way in the action. */ + plogx_dbg("Retransmit: outstanding = %d\n", outstanding_bytes); + plogx_dbg("Assuming %d->%d lost\n", ctx->ackd_seq, ctx->next_seq); + ctx->next_seq = ctx->ackd_seq; + plogx_dbg("highest seq from other side = %d\n", ctx->recv_seq); + } + /* When STREAM_CTX_F_MORE_DATA is set, real timeouts + can't occur. If this is needed, timeouts + need to carry additional information. */ + } + + /* The following code will retransmit the same data if next_seq is not moved forward. */ + uint32_t data_beg = ctx->next_seq - ctx->seq_first_byte; + uint32_t remaining_len = act->len - (data_beg - act->beg); + uint32_t data_len = remaining_len > ctx->other_mss? ctx->other_mss: remaining_len; + if (data_len == 0) + plogx_warn("data_len == 0\n"); + + if (remaining_len > ctx->other_mss) + ctx->flags |= STREAM_CTX_F_MORE_DATA; + else + ctx->flags &= ~STREAM_CTX_F_MORE_DATA; + + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, data_beg, data_len); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + if (ctx->flags & STREAM_CTX_F_MORE_DATA) + *next_tsc = tcp_resched_timeout(ctx); + else + tcp_retx_timeout_start(ctx, next_tsc); + + return 0; +} + +static int stream_tcp_proc_out_estab_rx(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + if (ctx->flags & STREAM_CTX_F_TCP_GOT_FIN) { + plogx_dbg("Got fin!\n"); + if (1) { + ctx->tcp_state = LAST_ACK; + create_tcp_pkt(ctx, mbuf, TCP_FIN_FLAG | TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } + else { + ctx->tcp_state = CLOSE_WAIT; + create_tcp_pkt(ctx, mbuf, TCP_FIN_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_resched_timeout(ctx); + return 0; + } + } + + if (ctx->flags & STREAM_CTX_F_NEW_DATA) + ctx->flags &= ~STREAM_CTX_F_NEW_DATA; + else { + ctx->same_state++; + tcp_set_retransmit(ctx); + plogx_dbg("state++ (ack = %d)\n", ctx->recv_seq); + } + + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; +} + +static int stream_tcp_proc_out_estab(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + if (ctx->stream_cfg->actions[ctx->cur_action].peer == ctx->peer) { + return stream_tcp_proc_out_estab_tx(ctx, mbuf, next_tsc); + } + else { + return stream_tcp_proc_out_estab_rx(ctx, mbuf, next_tsc); + } +} + +static int stream_tcp_proc_out_close_wait(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + /* CLOSE_WAIT is an intermediary stage that is only visited + when the FIN is sent after ACK'ing the incoming FIN. In any + case, it does not matter if there was a packet or not. */ + ctx->tcp_state = LAST_ACK; + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG | TCP_FIN_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; +} + +static int stream_tcp_proc_out_last_ack(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + if (ctx->ackd_seq == ctx->next_seq) { + plogx_dbg("Last ACK received\n"); + ctx->flags |= STREAM_CTX_F_TCP_ENDED; + return -1; + } + else { + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + if (ctx->flags & STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS) { + ctx->flags &= ~STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS; + *next_tsc = tcp_retx_timeout(ctx); + return -1; + } + + plogx_dbg("Retransmit!\n"); + ctx->next_seq = ctx->ackd_seq; + ctx->same_state++; + tcp_set_retransmit(ctx); + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG | TCP_FIN_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } +} + +static int stream_tcp_proc_out_fin_wait(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + if (ctx->ackd_seq == ctx->next_seq) { + if (ctx->flags & STREAM_CTX_F_TCP_GOT_FIN) { + ctx->same_state = 0; + ctx->tcp_state = TIME_WAIT; + ctx->sched_tsc = rte_rdtsc() + ctx->stream_cfg->tsc_timeout_time_wait; + plogx_dbg("from FIN_WAIT to TIME_WAIT\n"); + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = ctx->stream_cfg->tsc_timeout_time_wait; + return 0; + } + else { + /* FIN will still need to come */ + *next_tsc = tcp_retx_timeout(ctx); + return -1; + } + } + else { + if (ctx->flags & STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS) { + ctx->flags &= ~STREAM_CTX_F_LAST_RX_PKT_MADE_PROGRESS; + *next_tsc = tcp_retx_timeout(ctx); + return -1; + } + + plogx_dbg("Retransmit!\n"); + ctx->same_state++; + tcp_set_retransmit(ctx); + ctx->next_seq = ctx->ackd_seq; + create_tcp_pkt(ctx, mbuf, TCP_FIN_FLAG | TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = tcp_retx_timeout(ctx); + return 0; + } +} + +static int stream_tcp_proc_out_time_wait(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + if (ctx->sched_tsc < rte_rdtsc()) { + plogx_dbg("TIME_WAIT expired! for %#x\n", ctx->tuple->dst_addr); + ctx->flags |= STREAM_CTX_F_TCP_ENDED; + return -1; + } + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + *next_tsc = wait_tsc; + return -1; + } + + plogx_dbg("Got packet while in TIME_WAIT (pkt ACK reTX)\n"); + ctx->sched_tsc = rte_rdtsc() + ctx->stream_cfg->tsc_timeout_time_wait; + create_tcp_pkt(ctx, mbuf, TCP_ACK_FLAG, 0, 0); + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + *next_tsc = ctx->stream_cfg->tsc_timeout_time_wait; + return 0; +} + +static int stream_tcp_proc_out(struct stream_ctx *ctx, struct rte_mbuf *mbuf, uint64_t *next_tsc) +{ + if (ctx->same_state == 10) { + ctx->flags |= STREAM_CTX_F_EXPIRED; + return -1; + } + + switch (ctx->tcp_state) { + case CLOSED: /* Client initial state */ + return stream_tcp_proc_out_closed(ctx, mbuf, next_tsc); + case LISTEN: /* Server starts in this state. */ + return stream_tcp_proc_out_listen(ctx, mbuf, next_tsc); + case SYN_SENT: + return stream_tcp_proc_out_syn_sent(ctx, mbuf, next_tsc); + case SYN_RECEIVED: + return stream_tcp_proc_out_syn_recv(ctx, mbuf, next_tsc); + case ESTABLISHED: + return stream_tcp_proc_out_estab(ctx, mbuf, next_tsc); + case CLOSE_WAIT: + return stream_tcp_proc_out_close_wait(ctx, mbuf, next_tsc); + case LAST_ACK: + return stream_tcp_proc_out_last_ack(ctx, mbuf, next_tsc); + case FIN_WAIT: + return stream_tcp_proc_out_fin_wait(ctx, mbuf, next_tsc); + case TIME_WAIT: + return stream_tcp_proc_out_time_wait(ctx, mbuf, next_tsc); + } + + return -1; +} + +/* Return: zero: packet in mbuf is the reply, non-zero: data consumed, + nothing to send. The latter case might mean that the connection has + ended, or that a future event has been scheduled. l4_meta => + mbuf contains packet to be processed. */ +int stream_tcp_proc(struct stream_ctx *ctx, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, uint64_t *next_tsc) +{ + token_time_update(&ctx->token_time, rte_rdtsc()); + token_time_update(&ctx->token_time_other, rte_rdtsc()); + if (l4_meta) { + int ret; + + token_time_take_clamp(&ctx->token_time_other, mbuf_wire_size(mbuf)); + ret = stream_tcp_proc_in(ctx, l4_meta); + if (ret) + return ret; + } + + return stream_tcp_proc_out(ctx, mbuf, next_tsc); +} + +int stream_tcp_is_ended(struct stream_ctx *ctx) +{ + return ctx->flags & STREAM_CTX_F_TCP_ENDED; +} + +static void add_pkt_bytes(uint32_t *n_pkts, uint32_t *n_bytes, uint32_t len) +{ + len = (len < 60? 60 : len) + 20 + ETHER_CRC_LEN; + + (*n_pkts)++; + *n_bytes += len; +} + +void stream_tcp_calc_len(struct stream_cfg *cfg, uint32_t *n_pkts, uint32_t *n_bytes) +{ + const uint32_t client_hdr_len = cfg->data[PEER_CLIENT].hdr_len; + const uint32_t server_hdr_len = cfg->data[PEER_SERVER].hdr_len; + + *n_pkts = 0; + *n_bytes = 0; + + /* Connection setup */ + add_pkt_bytes(n_pkts, n_bytes, client_hdr_len + sizeof(struct tcp_hdr) + 4); /* SYN */ + add_pkt_bytes(n_pkts, n_bytes, server_hdr_len + sizeof(struct tcp_hdr) + 4); /* SYN/ACK */ + add_pkt_bytes(n_pkts, n_bytes, client_hdr_len + sizeof(struct tcp_hdr)); /* ACK */ + + for (uint32_t i = 0; i < cfg->n_actions; ++i) { + const uint32_t mss = 1440; /* TODO: should come from peer's own mss. */ + uint32_t remaining = cfg->actions[i].len; + const uint32_t send_hdr_len = cfg->actions[i].peer == PEER_CLIENT? client_hdr_len : server_hdr_len; + const uint32_t reply_hdr_len = cfg->actions[i].peer == PEER_CLIENT? server_hdr_len : client_hdr_len; + + if (remaining == 0) + break; + + while (remaining) { + uint32_t seg = remaining > mss? mss: remaining; + add_pkt_bytes(n_pkts, n_bytes, send_hdr_len + sizeof(struct tcp_hdr) + seg); + remaining -= seg; + } + + add_pkt_bytes(n_pkts, n_bytes, reply_hdr_len + sizeof(struct tcp_hdr)); + } + + /* Connection Tear-down */ + enum l4gen_peer last_peer = cfg->actions[cfg->n_actions - 1].peer; + + const uint32_t init_hdr_len = last_peer == PEER_CLIENT? client_hdr_len : server_hdr_len; + const uint32_t resp_hdr_len = last_peer == PEER_CLIENT? server_hdr_len : client_hdr_len; + + add_pkt_bytes(n_pkts, n_bytes, init_hdr_len + sizeof(struct tcp_hdr)); /* FIN */ + add_pkt_bytes(n_pkts, n_bytes, resp_hdr_len + sizeof(struct tcp_hdr)); /* FIN/ACK */ + add_pkt_bytes(n_pkts, n_bytes, init_hdr_len + sizeof(struct tcp_hdr)); /* ACK */ +} diff --git a/VNFs/DPPD-PROX/genl4_stream_tcp.h b/VNFs/DPPD-PROX/genl4_stream_tcp.h new file mode 100644 index 00000000..f7b04d51 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_stream_tcp.h @@ -0,0 +1,29 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _GENL4_STREAM_TCP_H_ +#define _GENL4_STREAM_TCP_H_ + +#include "genl4_stream.h" + +int stream_tcp_proc(struct stream_ctx *ctx, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, uint64_t *next_tsc); +int stream_tcp_is_ended(struct stream_ctx *ctx); +uint16_t stream_tcp_reply_len(struct stream_ctx *ctx); +void stream_tcp_calc_len(struct stream_cfg *cfg, uint32_t *n_pkts, uint32_t *n_bytes); + +void stream_tcp_create_rst(struct rte_mbuf *mbuf, struct l4_meta *l4_meta, struct pkt_tuple *tuple); + +#endif /* _GENL4_STREAM_TCP_H_ */ diff --git a/VNFs/DPPD-PROX/genl4_stream_udp.c b/VNFs/DPPD-PROX/genl4_stream_udp.c new file mode 100644 index 00000000..3de2db09 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_stream_udp.c @@ -0,0 +1,165 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "genl4_stream_udp.h" +#include "mbuf_utils.h" + +int stream_udp_is_ended(struct stream_ctx *ctx) +{ + return ctx->cur_action == ctx->stream_cfg->n_actions; +} + +static void update_token_times(struct stream_ctx *ctx) +{ + uint64_t now = rte_rdtsc(); + + token_time_update(&ctx->token_time_other, now); + token_time_update(&ctx->token_time, now); +} + +int stream_udp_proc(struct stream_ctx *ctx, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, uint64_t *next_tsc) +{ + update_token_times(ctx); + + if (l4_meta) { + enum l4gen_peer peer = ctx->stream_cfg->actions[ctx->cur_action].peer; + plogx_dbg("Consuming UDP data\n"); + /* data should come from the other side */ + if (peer == ctx->peer) { + plogx_err("Wrong peer\n"); + return -1; + } + /* Fixed length data expected */ + if (ctx->stream_cfg->actions[ctx->cur_action].len != l4_meta->len) { + plogx_dbg("unexpected UDP len (expected = %u, got = %u, action = %u)\n", + ctx->stream_cfg->actions[ctx->cur_action].len, + l4_meta->len, + ctx->cur_action); + + return -1; + } + /* With specific payload */ + if (memcmp(ctx->stream_cfg->data[peer].content + ctx->stream_cfg->actions[ctx->cur_action].beg, l4_meta->payload, l4_meta->len) != 0) { + plogx_dbg("Bad payload at action_id %d, with peer = %d and pos = %d and len=%d\n", ctx->cur_action, peer, ctx->cur_pos[peer], l4_meta->len); + return -1; + } + ctx->cur_pos[peer] += l4_meta->len; + ctx->cur_action++; + + if (stream_udp_is_ended(ctx)) + return -1; + + token_time_take(&ctx->token_time_other, mbuf_wire_size(mbuf)); + /* Time before next packet is expected to + arrive. Note, addition amount of time is accounted + for due to rate limiting. */ + uint64_t wait = token_time_tsc_until_full(&ctx->token_time_other); + *next_tsc = wait + ctx->stream_cfg->tsc_timeout; + } + + if (ctx->stream_cfg->actions[ctx->cur_action].peer != ctx->peer) { + const char *other_peer_str = ctx->peer != PEER_SERVER? "server" : "client"; + + plogx_dbg("Expecting more UDP data from %s, will expire = %s\n", other_peer_str, l4_meta == NULL? "yes" : "no"); + if (!l4_meta) { + ctx->flags |= STREAM_CTX_F_EXPIRED; + } + return -1; + } + + uint64_t wait_tsc = token_time_tsc_until_full(&ctx->token_time); + + if (wait_tsc != 0) { + plogx_dbg("Wait = %"PRIu64"\n", wait_tsc); + *next_tsc = wait_tsc; + return -1; + } + + const struct stream_cfg *stream_cfg = ctx->stream_cfg; + + uint8_t *pkt = rte_pktmbuf_mtod(mbuf, uint8_t *); + const struct peer_action *act = &stream_cfg->actions[ctx->cur_action]; + + uint16_t pkt_len = stream_cfg->data[act->peer].hdr_len + sizeof(struct udp_hdr) + act->len; + + rte_pktmbuf_pkt_len(mbuf) = pkt_len; + rte_pktmbuf_data_len(mbuf) = pkt_len; + plogx_dbg("Creating UDP data (peer = %s, payload len = %u)\n", act->peer == PEER_CLIENT? "client" : "server", act->len); + /* Construct the packet. The template is used up to L4 header, + a gap of sizeof(l4_hdr) is skipped, followed by the payload. */ + rte_memcpy(pkt, stream_cfg->data[act->peer].hdr, stream_cfg->data[act->peer].hdr_len); + rte_memcpy(pkt + stream_cfg->data[act->peer].hdr_len + sizeof(struct udp_hdr), stream_cfg->data[act->peer].content + act->beg, act->len); + + struct ipv4_hdr *l3_hdr = (struct ipv4_hdr*)&pkt[stream_cfg->data[act->peer].hdr_len - sizeof(struct ipv4_hdr)]; + struct udp_hdr *l4_hdr = (struct udp_hdr*)&pkt[stream_cfg->data[act->peer].hdr_len]; + + l3_hdr->src_addr = ctx->tuple->dst_addr; + l3_hdr->dst_addr = ctx->tuple->src_addr; + l3_hdr->next_proto_id = IPPROTO_UDP; + l4_hdr->src_port = ctx->tuple->dst_port; + l4_hdr->dst_port = ctx->tuple->src_port; + l4_hdr->dgram_len = rte_bswap16(sizeof(struct udp_hdr) + act->len); + /* TODO: UDP checksum calculation */ + l3_hdr->total_length = rte_bswap16(sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr) + act->len); + ctx->cur_pos[ctx->peer] += act->len; + ctx->cur_action++; + + /* When the stream has ended, there is no need to schedule + another timeout (which will be unscheduled at the end of + the stream). */ + if (stream_udp_is_ended(ctx)) + return 0; + + token_time_take(&ctx->token_time, mbuf_wire_size(mbuf)); + + /* Send next packet as soon as possible */ + if (ctx->stream_cfg->actions[ctx->cur_action].peer == ctx->peer) { + *next_tsc = token_time_tsc_until_full(&ctx->token_time); + } + else { + uint64_t wait = token_time_tsc_until_full(&ctx->token_time_other); + *next_tsc = wait + ctx->stream_cfg->tsc_timeout; + } + + return 0; +} + +uint16_t stream_udp_reply_len(struct stream_ctx *ctx) +{ + if (stream_udp_is_ended(ctx)) + return 0; + else if (ctx->stream_cfg->actions[ctx->cur_action].peer == ctx->peer) + return 0; + else + return ctx->stream_cfg->data[ctx->stream_cfg->actions[ctx->cur_action].peer].hdr_len + sizeof(struct udp_hdr) + + ctx->stream_cfg->actions[ctx->cur_action].len; +} + +void stream_udp_calc_len(struct stream_cfg *cfg, uint32_t *n_pkts, uint32_t *n_bytes) +{ + const uint32_t client_hdr_len = cfg->data[PEER_CLIENT].hdr_len; + const uint32_t server_hdr_len = cfg->data[PEER_SERVER].hdr_len; + + *n_pkts = 0; + *n_bytes = 0; + + for (uint32_t i = 0; i < cfg->n_actions; ++i) { + const uint32_t send_hdr_len = cfg->actions[i].peer == PEER_CLIENT? client_hdr_len : server_hdr_len; + uint32_t len = send_hdr_len + sizeof(struct udp_hdr) + cfg->actions[i].len; + *n_bytes += (len < 60? 60 : len) + 24; + (*n_pkts)++; + } +} diff --git a/VNFs/DPPD-PROX/genl4_stream_udp.h b/VNFs/DPPD-PROX/genl4_stream_udp.h new file mode 100644 index 00000000..c82d7951 --- /dev/null +++ b/VNFs/DPPD-PROX/genl4_stream_udp.h @@ -0,0 +1,28 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _GENL4_STREAM_UDP_H_ +#define _GENL4_STREAM_UDP_H_ + +#include "genl4_stream.h" + +int stream_udp_is_ended(struct stream_ctx *ctx); + +int stream_udp_proc(struct stream_ctx *ctx, struct rte_mbuf *mbuf, struct l4_meta *l4_meta, uint64_t *next_tsc); +uint16_t stream_udp_reply_len(struct stream_ctx *ctx); +void stream_udp_calc_len(struct stream_cfg *cfg, uint32_t *n_pkts, uint32_t *n_bytes); + +#endif /* _GENL4_STREAM_UDP_H_ */ diff --git a/VNFs/DPPD-PROX/gre.h b/VNFs/DPPD-PROX/gre.h new file mode 100644 index 00000000..23f68b62 --- /dev/null +++ b/VNFs/DPPD-PROX/gre.h @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _GRE_H_ +#define _GRE_H_ + +#define GRE_CRC_PRESENT 0x10 +#define GRE_ROUTING_PRESENT 0x08 +#define GRE_KEY_PRESENT 0x04 +#define GRE_SEQNUM_PRESENT 0x02 +#define GRE_STRICT_ROUTE 0x01 + +struct gre_hdr { + uint8_t recur: 3; /* recur */ + uint8_t bits: 5; /* bits: Checksum, Routing, Key, Sequence Number, strict Route */ + uint8_t version: 3; /* Version: must be 0 */ + uint8_t flags: 5; /* Flags: must be 0 */ + uint16_t type; /* Protocol type */ + uint32_t gre_id; /* Key ID */ +} __attribute__((__packed__)); + +#endif /* _GRE_H_ */ diff --git a/VNFs/DPPD-PROX/handle_acl.c b/VNFs/DPPD-PROX/handle_acl.c new file mode 100644 index 00000000..03949360 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_acl.c @@ -0,0 +1,314 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_acl.h> +#include <rte_ip.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "log.h" +#include "quit.h" +#include "parse_utils.h" +#include "ip_subnet.h" +#include "handle_acl.h" +#include "acl_field_def.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "prefetch.h" +#include "etypes.h" + +struct task_acl { + struct task_base base; + struct rte_acl_ctx *context; + const uint8_t *ptuples[64]; + + uint32_t n_rules; + uint32_t n_max_rules; + + void *field_defs; + size_t field_defs_size; + uint32_t n_field_defs; +}; + +static void set_tc(struct rte_mbuf *mbuf, uint32_t tc) +{ +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + uint32_t subport, pipe, traffic_class, queue; + enum rte_meter_color color; + + rte_sched_port_pkt_read_tree_path(mbuf, &subport, &pipe, &traffic_class, &queue); + color = rte_sched_port_pkt_read_color(mbuf); + + rte_sched_port_pkt_write(mbuf, subport, pipe, tc, queue, color); +#else + struct rte_sched_port_hierarchy *sched = + (struct rte_sched_port_hierarchy *) &mbuf->pkt.hash.sched; + sched->traffic_class = tc; +#endif +} + +static int handle_acl_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_acl *task = (struct task_acl *)tbase; + uint32_t results[64]; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + /* TODO: detect version_ihl != 0x45. Extract relevant + fields of that packet and point ptuples[j] to the + extracted verion. Note that this is very unlikely. */ + task->ptuples[j] = rte_pktmbuf_mtod(mbufs[j], uint8_t *); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + task->ptuples[j] = rte_pktmbuf_mtod(mbufs[j], uint8_t *); + } +#endif + + rte_acl_classify(task->context, (const uint8_t **)task->ptuples, results, n_pkts, 1); + + for (uint8_t i = 0; i < n_pkts; ++i) { + switch (results[i]) { + default: + case ACL_NOT_SET: + case ACL_DROP: + out[i] = OUT_DISCARD; + break; + case ACL_ALLOW: + out[i] = 0; + case ACL_RATE_LIMIT: + set_tc(mbufs[i], 3); + break; + }; + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void acl_msg(struct task_base *tbase, void **data, uint16_t n_msgs) +{ + struct task_acl *task = (struct task_acl *)tbase; + struct acl4_rule **new_rules = (struct acl4_rule **)data; + uint16_t i; + + for (i = 0; i < n_msgs; ++i) { + if (task->n_rules == task->n_max_rules) { + plog_err("Failed to add %d rule%s (already at maximum number of rules (%d))", + n_msgs - i, (n_msgs - i)? "s" : "", task->n_max_rules); + break; + } + + new_rules[i]->data.priority = ++task->n_rules; + rte_acl_add_rules(task->context, (struct rte_acl_rule*) new_rules[i], 1); + } + + /* No need to rebuild if no rules have been added */ + if (!i) { + return ; + } + + struct rte_acl_config acl_build_param; + /* Perform builds */ + acl_build_param.num_categories = 1; + + acl_build_param.num_fields = task->n_field_defs; + rte_memcpy(&acl_build_param.defs, task->field_defs, task->field_defs_size); + + int ret; + PROX_PANIC((ret = rte_acl_build(task->context, &acl_build_param)), + "Failed to build ACL trie (%d)\n", ret); +} + +static void init_task_acl(struct task_base *tbase, struct task_args *targ) +{ + struct task_acl *task = (struct task_acl *)tbase; + int use_qinq = targ->flags & TASK_ARG_QINQ_ACL; + + char name[PATH_MAX]; + struct rte_acl_param acl_param; + + /* Create ACL contexts */ + snprintf(name, sizeof(name), "acl-%d-%d", targ->lconf->id, targ->task); + + if (use_qinq) { + task->n_field_defs = RTE_DIM(pkt_qinq_ipv4_udp_defs); + task->field_defs = pkt_qinq_ipv4_udp_defs; + task->field_defs_size = sizeof(pkt_qinq_ipv4_udp_defs); + } else { + task->n_field_defs = RTE_DIM(pkt_eth_ipv4_udp_defs); + task->field_defs = pkt_eth_ipv4_udp_defs; + task->field_defs_size = sizeof(pkt_eth_ipv4_udp_defs); + } + + acl_param.name = name; + acl_param.socket_id = rte_lcore_to_socket_id(targ->lconf->id); + acl_param.rule_size = RTE_ACL_RULE_SZ(task->n_field_defs); + acl_param.max_rule_num = targ->n_max_rules; + + task->n_max_rules = targ->n_max_rules; + task->context = rte_acl_create(&acl_param); + + PROX_PANIC(task->context == NULL, "Failed to create ACL context\n"); + uint32_t free_rules = targ->n_max_rules; + + PROX_PANIC(!strcmp(targ->rules, ""), "No rule specified for ACL\n"); + + int ret = lua_to_rules(prox_lua(), GLOBAL, targ->rules, task->context, &free_rules, use_qinq, targ->qinq_tag); + PROX_PANIC(ret, "Failed to read rules from config:\n%s\n", get_lua_to_errors()); + task->n_rules = targ->n_max_rules - free_rules; + + plog_info("Configured %d rules\n", task->n_rules); + + if (task->n_rules) { + struct rte_acl_config acl_build_param; + /* Perform builds */ + acl_build_param.num_categories = 1; +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + acl_build_param.max_size = 0; +#endif + + acl_build_param.num_fields = task->n_field_defs; + rte_memcpy(&acl_build_param.defs, task->field_defs, task->field_defs_size); + + plog_info("Building trie structure\n"); + PROX_PANIC(rte_acl_build(task->context, &acl_build_param), + "Failed to build ACL trie\n"); + } + + targ->lconf->ctrl_timeout = freq_to_tsc(targ->ctrl_freq); + targ->lconf->ctrl_func_m[targ->task] = acl_msg; +} + +int str_to_rule(struct acl4_rule *rule, char** fields, int n_rules, int use_qinq) +{ + uint32_t svlan, svlan_mask; + uint32_t cvlan, cvlan_mask; + + uint32_t ip_proto, ip_proto_mask; + + struct ip4_subnet ip_src; + struct ip4_subnet ip_dst; + + uint32_t sport_lo, sport_hi; + uint32_t dport_lo, dport_hi; + + enum acl_action class = ACL_NOT_SET; + char class_str[24]; + + PROX_PANIC(parse_int_mask(&svlan, &svlan_mask, fields[0]), "Error parsing svlan: %s\n", get_parse_err()); + PROX_PANIC(parse_int_mask(&cvlan, &cvlan_mask, fields[1]), "Error parsing cvlan: %s\n", get_parse_err()); + PROX_PANIC(parse_int_mask(&ip_proto, &ip_proto_mask, fields[2]), "Error parsing ip protocol: %s\n", get_parse_err()); + PROX_PANIC(parse_ip4_cidr(&ip_src, fields[3]), "Error parsing source IP subnet: %s\n", get_parse_err()); + PROX_PANIC(parse_ip4_cidr(&ip_dst, fields[4]), "Error parsing dest IP subnet: %s\n", get_parse_err()); + + PROX_PANIC(parse_range(&sport_lo, &sport_hi, fields[5]), "Error parsing source port range: %s\n", get_parse_err()); + PROX_PANIC(parse_range(&dport_lo, &dport_hi, fields[6]), "Error parsing destination port range: %s\n", get_parse_err()); + + PROX_PANIC(parse_str(class_str, fields[7], sizeof(class_str)), "Error parsing action: %s\n", get_parse_err()); + + if (!strcmp(class_str, "drop")) { + class = ACL_DROP; + } + else if (!strcmp(class_str, "allow")) { + class = ACL_ALLOW; + } + else if (!strcmp(class_str, "rate limit")) { + class = ACL_RATE_LIMIT; + } + else { + plog_err("unknown class type: %s\n", class_str); + } + + rule->data.userdata = class; /* allow, drop or ratelimit */ + rule->data.category_mask = 1; + rule->data.priority = n_rules; + + /* Configuration for rules is done in little-endian so no bswap is needed here.. */ + + rule->fields[0].value.u8 = ip_proto; + rule->fields[0].mask_range.u8 = ip_proto_mask; + rule->fields[1].value.u32 = ip_src.ip; + rule->fields[1].mask_range.u32 = ip_src.prefix; + + rule->fields[2].value.u32 = ip_dst.ip; + rule->fields[2].mask_range.u32 = ip_dst.prefix; + + rule->fields[3].value.u16 = sport_lo; + rule->fields[3].mask_range.u16 = sport_hi; + + rule->fields[4].value.u16 = dport_lo; + rule->fields[4].mask_range.u16 = dport_hi; + + if (use_qinq) { + rule->fields[5].value.u16 = rte_bswap16(ETYPE_8021ad); + rule->fields[5].mask_range.u16 = 0xffff; + + /* To mask out the TCI and only keep the VID, the mask should be 0x0fff */ + rule->fields[6].value.u16 = svlan; + rule->fields[6].mask_range.u16 = svlan_mask; + + rule->fields[7].value.u16 = rte_bswap16(ETYPE_VLAN); + rule->fields[7].mask_range.u16 = 0xffff; + + rule->fields[8].value.u16 = cvlan; + rule->fields[8].mask_range.u16 = cvlan_mask; + } + else { + /* Reuse first ethertype from vlan to check if packet is IPv4 packet */ + rule->fields[5].value.u16 = rte_bswap16(ETYPE_IPv4); + rule->fields[5].mask_range.u16 = 0xffff; + + /* Other fields are ignored */ + rule->fields[6].value.u16 = 0; + rule->fields[6].mask_range.u16 = 0; + rule->fields[7].value.u16 = 0; + rule->fields[7].mask_range.u16 = 0; + rule->fields[8].value.u16 = 0; + rule->fields[8].mask_range.u16 = 0; + } + return 0; +} + +static struct task_init task_init_acl = { + .mode_str = "acl", + .init = init_task_acl, + .handle = handle_acl_bulk, + .size = sizeof(struct task_acl) +}; + +__attribute__((constructor)) static void reg_task_acl(void) +{ + reg_task(&task_init_acl); +} diff --git a/VNFs/DPPD-PROX/handle_acl.h b/VNFs/DPPD-PROX/handle_acl.h new file mode 100644 index 00000000..8e4c140c --- /dev/null +++ b/VNFs/DPPD-PROX/handle_acl.h @@ -0,0 +1,29 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_ACL_H_ +#define _HANDLE_ACL_H_ + +#include <rte_acl.h> + +struct acl4_rule { + struct rte_acl_rule_data data; + struct rte_acl_field fields[9]; +}; + +int str_to_rule(struct acl4_rule *rule, char** fields, int n_rules, int use_qinq); + +#endif /* _HANDLE_ACL_H_ */ diff --git a/VNFs/DPPD-PROX/handle_aggregator.c b/VNFs/DPPD-PROX/handle_aggregator.c new file mode 100644 index 00000000..6434d759 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_aggregator.c @@ -0,0 +1,229 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> +#include <stdio.h> +#include <string.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "lconf.h" +#include "task_base.h" +#include "task_init.h" +#include "defines.h" +#include "prefetch.h" +#include "qinq.h" +#include "prox_cfg.h" +#include "log.h" +#include "quit.h" +#include "prox_shared.h" +#include "mbuf_utils.h" +#include "handle_aggregator.h" + +#define PRIORITY_DHCP (HIGH_PRIORITY) + +#define TASK_STATS_ADD_DROP_TX_FAIL_PRIO(stats, ntx, prio) do { \ + (stats)->drop_tx_fail_prio[prio] += ntx; \ + } while(0) +#define TASK_STATS_ADD_TX_PRIO(stats, ntx, prio) do { \ + (stats)->rx_prio[prio] += ntx; \ + } while(0) \ + +static inline uint8_t detect_l4_priority(uint8_t l3_priority, const struct ipv4_hdr *ipv4_hdr) +{ + if (ipv4_hdr->next_proto_id == IPPROTO_UDP) { + const struct udp_hdr *udp = (const struct udp_hdr *)((const uint8_t *)ipv4_hdr + sizeof(struct ipv4_hdr)); + if (((udp->src_port == 0x67) && (udp->dst_port == 0x68)) || ((udp->src_port == 0x68) && (udp->dst_port == 0x67))) { + return PRIORITY_DHCP; + } + } + return l3_priority; +} + +static inline uint8_t detect_l3_priority(uint8_t l2_priority, const struct ipv4_hdr *ipv4_hdr) +{ + uint8_t dscp; + if ((ipv4_hdr->version_ihl >> 4) == 4) { + } else if ((ipv4_hdr->version_ihl >> 4) == 6) { + plog_warn("IPv6 Not implemented\n"); + return OUT_DISCARD; + } else { + plog_warn("Unexpected IP version\n"); + return OUT_DISCARD; + } + dscp = ipv4_hdr->type_of_service >> 2; + if (dscp) + return MAX_PRIORITIES - dscp - 1; + else + return l2_priority; +} + +static inline uint8_t detect_l2_priority(const struct qinq_hdr *pqinq) +{ + if (pqinq->cvlan.eth_proto != ETYPE_VLAN) { + plog_warn("Unexpected proto in QinQ = %#04x\n", pqinq->cvlan.eth_proto); + return OUT_DISCARD; + } + uint16_t svlan_priority = ntohs(pqinq->svlan.vlan_tci >> 13); + uint16_t cvlan_priority = ntohs(pqinq->cvlan.vlan_tci >> 13); + if (svlan_priority) + return svlan_priority; + else + return cvlan_priority; +} + +static inline void buffer_packet(struct task_aggregator *task, struct rte_mbuf *mbuf, uint8_t priority) +{ + struct task_base *tbase = (struct task_base *)task; + + struct task_buffer *prio = &task->priority[priority]; + if (prio->pkt_nb < BUFFER_LENGTH) { + prio->buffer[prio->pkt_pos] = mbuf; + prio->pkt_pos++; + if (prio->pkt_pos == BUFFER_LENGTH) + prio->pkt_pos = 0; + prio->pkt_nb++; + } else { + task->drop.buffer[task->drop.pkt_nb] = mbuf; + task->drop.pkt_nb++; + TASK_STATS_ADD_DROP_TX_FAIL_PRIO(&task->stats, 1, priority); + } +} + +static inline void handle_aggregator(struct task_aggregator *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + uint8_t priority = 0; + const struct qinq_hdr *pqinq; + const struct ipv4_hdr *ipv4_hdr; + + const uint16_t eth_type = peth->ether_type; + switch (eth_type) { + case ETYPE_MPLSU: + case ETYPE_MPLSM: + break; + case ETYPE_8021ad: + pqinq = rte_pktmbuf_mtod(mbuf, const struct qinq_hdr *); + if ((priority = detect_l2_priority(pqinq)) == OUT_DISCARD) + break; + ipv4_hdr = (const struct ipv4_hdr *)(pqinq + 1); + if ((priority = detect_l3_priority(priority, ipv4_hdr)) == OUT_DISCARD) + break; + if ((priority = detect_l4_priority(priority, ipv4_hdr)) == OUT_DISCARD) + break; + break; + case ETYPE_VLAN: + break; + case ETYPE_IPv4: + ipv4_hdr = (const struct ipv4_hdr *)(peth+1); + if ((priority = detect_l3_priority(LOW_PRIORITY, ipv4_hdr)) == OUT_DISCARD) + break; + if ((priority = detect_l4_priority(priority, ipv4_hdr)) == OUT_DISCARD) + break; + break; + case ETYPE_IPv6: + break; + case ETYPE_ARP: + break; + default: + break; + } + if (priority == OUT_DISCARD) { + task->drop.buffer[task->drop.pkt_nb] = mbuf; + task->drop.pkt_nb++; + return; + } + buffer_packet(task, mbuf, priority); +} + +static int handle_aggregator_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_aggregator *task = (struct task_aggregator *)tbase; + + uint16_t j; + uint32_t drop_bytes = 0; +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(mbufs[j + PREFETCH_OFFSET]); + prefetch_nta(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + handle_aggregator(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + handle_aggregator(task, mbufs[j]); + } +#endif + + for (int i = 0 ; i < task->drop.pkt_nb; i++) { + drop_bytes += mbuf_wire_size(task->drop.buffer[i]); + rte_pktmbuf_free(task->drop.buffer[i]); + } + TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, task->drop.pkt_nb); + TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes); + task->drop.pkt_nb = 0; + + for (int priority = 0; priority < MAX_PRIORITIES; priority++) { + struct task_buffer *prio = &task->priority[priority]; + if (prio->pkt_nb) { + uint8_t n = 0; + if (prio->pkt_pos > prio->pkt_nb) { + struct rte_mbuf **buf = prio->buffer + prio->pkt_pos - prio->pkt_nb; + n = tbase->aux->tx_pkt_try(&task->base, buf, prio->pkt_nb); + } else { + struct rte_mbuf **buf = prio->buffer + BUFFER_LENGTH + prio->pkt_pos - prio->pkt_nb; + n = tbase->aux->tx_pkt_try(&task->base, buf, prio->pkt_nb - prio->pkt_pos); + if (n == (prio->pkt_nb - prio->pkt_pos)) + n += tbase->aux->tx_pkt_try(&task->base, prio->buffer, prio->pkt_pos); + } + prio->pkt_nb -=n; + TASK_STATS_ADD_TX_PRIO(&task->stats, n, priority); + if (prio->pkt_nb) + break; + } + } + return 0; +} + +static void init_task_aggregator(struct task_base *tbase, struct task_args *targ) +{ + struct task_aggregator *task = (struct task_aggregator *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); +} + +static struct task_init task_init_aggregator = { + .mode_str = "aggreg", + .init = init_task_aggregator, + .handle = handle_aggregator_bulk, + .flag_features = TASK_FEATURE_NEVER_DISCARDS, + .size = sizeof(struct task_aggregator) +}; + +__attribute__((constructor)) static void reg_task_aggregator(void) +{ + reg_task(&task_init_aggregator); +} diff --git a/VNFs/DPPD-PROX/handle_aggregator.h b/VNFs/DPPD-PROX/handle_aggregator.h new file mode 100644 index 00000000..1d5cd6c9 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_aggregator.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_AGGREGATOR_H_ +#define _HANDLE_AGGREGATOR_H_ + +#include "task_base.h" +#include "task_init.h" +#include "stats_prio_task.h" + +#define MAX_PRIORITIES 8 +#define LOW_PRIORITY (MAX_PRIORITIES - 1) +#define HIGH_PRIORITY 0 +#define BUFFER_LENGTH 256 + +struct task_buffer { + struct rte_mbuf *buffer[BUFFER_LENGTH]; + uint16_t pkt_pos; + uint16_t pkt_nb; +}; + +struct task_aggregator { + struct task_base base; + struct prio_task_rt_stats stats; + struct task_buffer priority[MAX_PRIORITIES]; + struct task_buffer drop; +}; + +#endif /* _HANDLE_AGGREGATOR_H_ */ diff --git a/VNFs/DPPD-PROX/handle_arp.c b/VNFs/DPPD-PROX/handle_arp.c new file mode 100644 index 00000000..106e19e5 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_arp.c @@ -0,0 +1,181 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "task_init.h" +#include "task_base.h" +#include "stats.h" +#include "arp.h" +#include "etypes.h" +#include "quit.h" +#include "log.h" +#include "prox_port_cfg.h" +#include "lconf.h" +#include "cmd_parser.h" +#include "handle_arp.h" + +struct task_arp { + struct task_base base; + struct ether_addr src_mac; + uint32_t seed; + uint32_t flags; + uint32_t ip; + uint32_t tmp_ip; + uint8_t arp_replies_ring; + uint8_t other_pkts_ring; + uint8_t send_arp_requests; +}; + +static void task_update_config(struct task_arp *task) +{ + if (unlikely(task->ip != task->tmp_ip)) + task->ip = task->tmp_ip; +} + +static void handle_arp(struct task_arp *task, struct ether_hdr_arp *hdr, struct ether_addr *s_addr) +{ + prepare_arp_reply(hdr, s_addr); + memcpy(hdr->ether_hdr.d_addr.addr_bytes, hdr->ether_hdr.s_addr.addr_bytes, 6); + memcpy(hdr->ether_hdr.s_addr.addr_bytes, s_addr, 6); +} + +static int handle_arp_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct ether_hdr_arp *hdr; + struct task_arp *task = (struct task_arp *)tbase; + uint8_t out[MAX_PKT_BURST] = {0}; + struct rte_mbuf *replies_mbufs[64] = {0}, *arp_pkt_mbufs[64] = {0}; + int n_arp_reply_pkts = 0, n_other_pkts = 0,n_arp_pkts = 0; + struct ether_addr s_addr; + + for (uint16_t j = 0; j < n_pkts; ++j) { + hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr_arp *); + if (hdr->ether_hdr.ether_type == ETYPE_ARP) { + if (arp_is_gratuitous(hdr)) { + out[n_other_pkts] = OUT_DISCARD; + n_other_pkts++; + plog_info("Received gratuitous packet \n"); + } else if (hdr->arp.oper == 0x100) { + if (task->arp_replies_ring != OUT_DISCARD) { + arp_pkt_mbufs[n_arp_pkts] = mbufs[j]; + out[n_arp_pkts] = task->arp_replies_ring; + n_arp_pkts++; + } else if (task->ip == 0) { + create_mac(hdr, &s_addr); + handle_arp(task, hdr, &s_addr); + replies_mbufs[n_arp_reply_pkts] = mbufs[j]; + out[n_arp_reply_pkts] = 0; + n_arp_reply_pkts++; + } else if (hdr->arp.data.tpa == task->ip) { + handle_arp(task, hdr, &task->src_mac); + replies_mbufs[n_arp_reply_pkts] = mbufs[j]; + out[n_arp_reply_pkts] = 0; + n_arp_reply_pkts++; + } else { + out[n_other_pkts] = OUT_DISCARD; + mbufs[n_other_pkts] = mbufs[j]; + n_other_pkts++; + plogx_dbg("Received ARP on unexpected IP %x, expecting %x\n", rte_be_to_cpu_32(hdr->arp.data.tpa), rte_be_to_cpu_32(task->ip)); + } + } else if (hdr->arp.oper == 0x200) { + arp_pkt_mbufs[n_arp_pkts] = mbufs[j]; + out[n_arp_pkts] = task->arp_replies_ring; + n_arp_pkts++; + } else { + out[n_other_pkts] = task->other_pkts_ring; + mbufs[n_other_pkts] = mbufs[j]; + n_other_pkts++; + } + } else { + out[n_other_pkts] = task->other_pkts_ring; + mbufs[n_other_pkts] = mbufs[j]; + n_other_pkts++; + } + } + int ret = 0; + + if (n_arp_reply_pkts) { + ret+=task->base.aux->tx_pkt_hw(&task->base, replies_mbufs, n_arp_reply_pkts, out); + } + if (n_arp_pkts) + ret+= task->base.tx_pkt(&task->base, arp_pkt_mbufs, n_arp_pkts, out); + ret+= task->base.tx_pkt(&task->base, mbufs, n_other_pkts, out); + task_update_config(task); + return ret; +} + +void task_arp_set_local_ip(struct task_base *tbase, uint32_t ip) +{ + struct task_arp *task = (struct task_arp *)tbase; + task->tmp_ip = ip; +} + +static void init_task_arp(struct task_base *tbase, struct task_args *targ) +{ + struct task_arp *task = (struct task_arp *)tbase; + struct task_args *dtarg; + struct core_task ct; + int port_found = 0; + task->other_pkts_ring = OUT_DISCARD; + task->arp_replies_ring = OUT_DISCARD; + + task->seed = rte_rdtsc(); + memcpy(&task->src_mac, &prox_port_cfg[task->base.tx_params_hw_sw.tx_port_queue.port].eth_addr, sizeof(struct ether_addr)); + + task->ip = rte_cpu_to_be_32(targ->local_ipv4); + task->tmp_ip = task->ip; + + PROX_PANIC(targ->nb_txrings > targ->core_task_set[0].n_elems, "%d txrings but %d elems in task_set\n", targ->nb_txrings, targ->core_task_set[0].n_elems); + for (uint32_t i = 0; i < targ->nb_txrings; ++i) { + ct = targ->core_task_set[0].core_task[i]; + plog_info("ARP mode checking whether core %d task %d (i.e. ring %d) can handle arp\n", ct.core, ct.task, i); + dtarg = core_targ_get(ct.core, ct.task); + dtarg = find_reachable_task_sending_to_port(dtarg); + if ((dtarg != NULL) && (task_is_sub_mode(dtarg->lconf->id, dtarg->id, "l3"))) { + plog_info("ARP task sending ARP replies to core %d and task %d to handle them\n", ct.core, ct.task); + task->arp_replies_ring = i; + } else { + plog_info("ARP task sending (potentially other) packets to core %d and task %d\n", ct.core, ct.task); + task->other_pkts_ring = i; + } + } + + if ((targ->nb_txports == 0) && (task->arp_replies_ring == OUT_DISCARD)) { + PROX_PANIC(1, "arp mode must have a tx_port or a ring able to a task in l3 reaching tx port"); + } +} + +// Reply to ARP requests with random MAC addresses +static struct task_init task_init_cpe_arp = { + .mode_str = "arp", + .init = init_task_arp, + .handle = handle_arp_bulk, + .size = sizeof(struct task_arp) +}; + +// Reply to ARP requests with MAC address of the interface +static struct task_init task_init_arp = { + .mode_str = "arp", + .sub_mode_str = "local", + .init = init_task_arp, + .handle = handle_arp_bulk, + .size = sizeof(struct task_arp) +}; + +__attribute__((constructor)) static void reg_task_arp(void) +{ + reg_task(&task_init_cpe_arp); + reg_task(&task_init_arp); +} diff --git a/VNFs/DPPD-PROX/handle_arp.h b/VNFs/DPPD-PROX/handle_arp.h new file mode 100644 index 00000000..0cde22ae --- /dev/null +++ b/VNFs/DPPD-PROX/handle_arp.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_ARP_H_ +#define _HANDLE_ARP_H_ + +struct task_base; +void task_arp_set_local_ip(struct task_base *tbase, uint32_t ip); + +#endif /* _HANDLE_ARP_H_ */ diff --git a/VNFs/DPPD-PROX/handle_blockudp.c b/VNFs/DPPD-PROX/handle_blockudp.c new file mode 100644 index 00000000..04c945e5 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_blockudp.c @@ -0,0 +1,61 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> +#include <rte_ether.h> + +#include "task_base.h" +#include "task_init.h" +#include "defines.h" +#include "etypes.h" +#include "prefetch.h" +#include "log.h" + +struct task_blockudp { + struct task_base base; +}; + +static int handle_blockudp_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_blockudp *task = (struct task_blockudp *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + for (j = 0; j < n_pkts; ++j) { + struct ether_hdr *peth = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *); + struct ipv4_hdr *pip = (struct ipv4_hdr *) (peth + 1); + out[j] = peth->ether_type == ETYPE_IPv4 && pip->next_proto_id == 0x11 ? OUT_DISCARD : 0; + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void init_task_blockudp(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ +} + +static struct task_init task_init_blockudp = { + .mode_str = "blockudp", + .init = init_task_blockudp, + .handle = handle_blockudp_bulk, + .size = sizeof(struct task_blockudp) +}; + +__attribute__((constructor)) static void reg_task_blockudp(void) +{ + reg_task(&task_init_blockudp); +} diff --git a/VNFs/DPPD-PROX/handle_cgnat.c b/VNFs/DPPD-PROX/handle_cgnat.c new file mode 100644 index 00000000..6f176c08 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_cgnat.c @@ -0,0 +1,987 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_hash.h> +#include <rte_hash_crc.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_version.h> +#include <rte_byteorder.h> +#include <rte_lpm.h> + +#include "prox_lua_types.h" +#include "prox_lua.h" +#include "prox_malloc.h" +#include "prox_cksum.h" +#include "prefetch.h" +#include "etypes.h" +#include "log.h" +#include "quit.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "prox_port_cfg.h" +#include "hash_entry_types.h" +#include "prox_shared.h" +#include "handle_cgnat.h" + +#define ALL_32_BITS 0xffffffff +#define BIT_16_TO_31 0xffff0000 +#define BIT_8_TO_15 0x0000ff00 +#define BIT_0_TO_15 0x0000ffff + +#define IP4(x) x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >> 24 + +struct private_key { + uint32_t ip_addr; + uint16_t l4_port; +} __attribute__((packed)); + +struct private_flow_entry { + uint64_t flow_time; + uint32_t ip_addr; + uint32_t private_ip_idx; + uint16_t l4_port; +}; + +struct public_key { + uint32_t ip_addr; + uint16_t l4_port; +} __attribute__((packed)); + +struct public_entry { + uint32_t ip_addr; + uint16_t l4_port; + uint32_t private_ip_idx; + uint8_t dpdk_port; +}; + +struct public_ip_config_info { + uint32_t public_ip; + uint32_t max_port_count; + uint32_t port_free_count; + uint16_t *port_list; +}; + +struct private_ip_info { + uint64_t mac_aging_time; + uint32_t public_ip; + uint32_t public_ip_idx; + struct rte_ether *private_mac; + uint8_t static_entry; +}; + +struct task_nat { + struct task_base base; + struct rte_hash *private_ip_hash; + struct rte_hash *private_ip_port_hash; + struct rte_hash *public_ip_port_hash; + struct private_flow_entry *private_flow_entries; + struct public_entry *public_entries; + struct next_hop *next_hops; + struct lcore_cfg *lconf; + struct rte_lpm *ipv4_lpm; + uint32_t total_free_port_count; + uint32_t number_free_rules; + int private; + uint32_t public_ip_count; + uint32_t last_ip; + struct public_ip_config_info *public_ip_config_info; + struct private_ip_info *private_ip_info; + uint8_t runtime_flags; + int offload_crc; + uint64_t src_mac[PROX_MAX_PORTS]; + uint64_t src_mac_from_dpdk_port[PROX_MAX_PORTS]; + volatile int dump_public_hash; + volatile int dump_private_hash; +}; +static __m128i proto_ipsrc_portsrc_mask; +static __m128i proto_ipdst_portdst_mask; +struct pkt_eth_ipv4 { + struct ether_hdr ether_hdr; + struct ipv4_hdr ipv4_hdr; + struct udp_hdr udp_hdr; +} __attribute__((packed)); + +void task_cgnat_dump_public_hash(struct task_nat *task) +{ + task->dump_public_hash = 1; +} + +void task_cgnat_dump_private_hash(struct task_nat *task) +{ + task->dump_private_hash = 1; +} + +static void set_l2(struct task_nat *task, struct rte_mbuf *mbuf, uint8_t nh_idx) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + *((uint64_t *)(&peth->d_addr)) = task->next_hops[nh_idx].mac_port_8bytes; + *((uint64_t *)(&peth->s_addr)) = task->src_mac[task->next_hops[nh_idx].mac_port.out_idx]; +} + +static uint8_t route_ipv4(struct task_nat *task, struct rte_mbuf *mbuf) +{ + struct pkt_eth_ipv4 *pkt = rte_pktmbuf_mtod(mbuf, struct pkt_eth_ipv4 *); + struct ipv4_hdr *ip = &pkt->ipv4_hdr; + struct ether_hdr *peth_out; + uint8_t tx_port; + uint32_t dst_ip; + + switch(ip->next_proto_id) { + case IPPROTO_TCP: + case IPPROTO_UDP: + dst_ip = ip->dst_addr; + break; + default: + /* Routing for other protocols is not implemented */ + plogx_info("Routing nit implemented for this protocol\n"); + return OUT_DISCARD; + } + +#if RTE_VERSION >= RTE_VERSION_NUM(16,4,0,1) + uint32_t next_hop_index; +#else + uint8_t next_hop_index; +#endif + if (unlikely(rte_lpm_lookup(task->ipv4_lpm, rte_bswap32(dst_ip), &next_hop_index) != 0)) { + uint8_t* dst_ipp = (uint8_t*)&dst_ip; + plog_warn("lpm_lookup failed for ip %d.%d.%d.%d: rc = %d\n", + dst_ipp[0], dst_ipp[1], dst_ipp[2], dst_ipp[3], -ENOENT); + return OUT_DISCARD; + } + + tx_port = task->next_hops[next_hop_index].mac_port.out_idx; + set_l2(task, mbuf, next_hop_index); + return tx_port; +} + +static int release_ip(struct task_nat *task, uint32_t *ip_addr, int public_ip_idx) +{ + return 0; +} + +static int release_port(struct task_nat *task, uint32_t public_ip_idx, uint16_t udp_src_port) +{ + struct public_ip_config_info *public_ip_config_info = &task->public_ip_config_info[public_ip_idx]; + if (public_ip_config_info->max_port_count > public_ip_config_info->port_free_count) { + public_ip_config_info->port_list[public_ip_config_info->port_free_count] = udp_src_port; + public_ip_config_info->port_free_count++; + task->total_free_port_count ++; + plogx_dbg("Now %d free ports for IP %d.%d.%d.%d\n", public_ip_config_info->port_free_count, IP4(public_ip_config_info->public_ip)); + } else { + plogx_err("Unable to release port for ip index %d: max_port_count = %d, port_free_count = %d", public_ip_idx, public_ip_config_info->max_port_count, public_ip_config_info->port_free_count); + return -1; + } + return 0; +} + +static int get_new_ip(struct task_nat *task, uint32_t *ip_addr) +{ + struct public_ip_config_info *ip_info; + if (++task->last_ip >= task->public_ip_count) + task->last_ip = 0; + for (uint32_t ip_idx = task->last_ip; ip_idx < task->public_ip_count; ip_idx++) { + ip_info = &task->public_ip_config_info[ip_idx]; + plogx_dbg("Checking public IP index %d\n", ip_idx); + if ((ip_info->port_free_count) > 0) { + plogx_dbg("Public IP index %d (IP %d.%d.%d.%d) has %d free ports\n", ip_idx, IP4(ip_info->public_ip), ip_info->port_free_count); + *ip_addr = ip_info->public_ip; + task->last_ip = ip_idx; + return ip_idx; + } + } + for (uint32_t ip_idx = 0; ip_idx < task->last_ip; ip_idx++) { + ip_info = &task->public_ip_config_info[ip_idx]; + if ((ip_info->port_free_count) > 0) { + plogx_dbg("Public IP index %d (IP %d.%d.%d.%d) has %d free ports\n", ip_idx, IP4(ip_info->public_ip), ip_info->port_free_count); + *ip_addr = ip_info->public_ip; + task->last_ip = ip_idx; + return ip_idx; + } + } + return -1; +} + +static int get_new_port(struct task_nat *task, uint32_t ip_idx, uint16_t *udp_src_port) +{ + int ret; + struct public_ip_config_info *public_ip_config_info = &task->public_ip_config_info[ip_idx]; + if (public_ip_config_info->port_free_count > 0) { + public_ip_config_info->port_free_count--; + *udp_src_port = public_ip_config_info->port_list[public_ip_config_info->port_free_count]; + task->total_free_port_count --; + plogx_info("Now %d free ports for IP %d.%d.%d.%d\n", public_ip_config_info->port_free_count, IP4(public_ip_config_info->public_ip)); + } else + return -1; + return 0; +} + +static int delete_port_entry(struct task_nat *task, uint8_t proto, uint32_t private_ip, uint16_t private_port, uint32_t public_ip, uint16_t public_port, int public_ip_idx) +{ + int ret; + struct private_key private_key; + struct public_key public_key; +// private_key.proto = proto; + private_key.ip_addr = private_ip; + private_key.l4_port = private_port; + ret = rte_hash_del_key(task->private_ip_port_hash, (const void *)&private_key); + if (ret < 0) { + plogx_info("Unable delete key ip %d.%d.%d.%d / port %x in private ip_port hash\n", IP4(private_ip), private_port); + return -1; + } else { + plogx_dbg("Deleted ip %d.%d.%d.%d / port %x from private ip_port hash\n", IP4(private_ip), private_port); + } + public_key.ip_addr = public_ip; + public_key.l4_port = public_port; + ret = rte_hash_del_key(task->public_ip_port_hash, (const void *)&public_key); + if (ret < 0) { + plogx_info("Unable delete key ip %d.%d.%d.%d / port %x in public ip_port hash\n", IP4(public_ip), public_port); + return -1; + } else { + plogx_dbg("Deleted ip %d.%d.%d.%d / port %x (hash index %d) from public ip_port hash\n", IP4(public_ip), public_port, ret); + release_port(task, public_ip_idx, public_port); + } + return 0; +} + +static int add_new_port_entry(struct task_nat *task, uint8_t proto, int public_ip_idx, int private_ip_idx, uint32_t private_src_ip, uint16_t private_udp_port, struct rte_mbuf *mbuf, uint64_t tsc, uint16_t *port) +{ + struct private_key private_key; + struct public_key public_key; + uint32_t ip = task->public_ip_config_info[public_ip_idx].public_ip; + int ret; + if (get_new_port(task, public_ip_idx, port) < 0) { + plogx_info("Unable to find new port for IP %x\n", private_src_ip); + return -1; + } +// private_key.proto = proto; + private_key.ip_addr = private_src_ip; + private_key.l4_port = private_udp_port; + ret = rte_hash_add_key(task->private_ip_port_hash, (const void *)&private_key); + if (ret < 0) { + plogx_info("Unable add ip %d.%d.%d.%d / port %x in private ip_port hash\n", IP4(private_src_ip), private_udp_port); + release_port(task, public_ip_idx, *port); + return -1; + } else if (task->private_flow_entries[ret].ip_addr) { + plogx_dbg("Race condition properly handled: port alrerady added\n"); + release_port(task, public_ip_idx, *port); + return ret; + } else { + plogx_dbg("Added ip %d.%d.%d.%d / port %x in private ip_port hash => %d.%d.%d.%d / %d - index = %d\n", IP4(private_src_ip), private_udp_port, IP4(ip), *port, ret); + } + task->private_flow_entries[ret].ip_addr = ip; + task->private_flow_entries[ret].l4_port = *port; + task->private_flow_entries[ret].flow_time = tsc; + task->private_flow_entries[ret].private_ip_idx = private_ip_idx; + + public_key.ip_addr = ip; + public_key.l4_port = *port; + plogx_dbg("Adding key ip %d.%d.%d.%d / port %x in public ip_port hash\n", IP4(ip), *port); + ret = rte_hash_add_key(task->public_ip_port_hash, (const void *)&public_key); + if (ret < 0) { + plogx_info("Unable add ip %x / port %x in public ip_port hash\n", ip, *port); + // TODO: remove from private_ip_port_hash + release_port(task, public_ip_idx, *port); + return -1; + } else { + plogx_dbg("Added ip %d.%d.%d.%d / port %x in public ip_port hash\n", IP4(ip), *port); + } + task->public_entries[ret].ip_addr = private_src_ip; + task->public_entries[ret].l4_port = private_udp_port; + task->public_entries[ret].dpdk_port = mbuf->port; + task->public_entries[ret].private_ip_idx = private_ip_idx; + return ret; +} + +static int handle_nat_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_nat *task = (struct task_nat *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + uint32_t *ip_addr, public_ip, private_ip; + uint16_t *udp_src_port, port, private_port, public_port; + struct pkt_eth_ipv4 *pkt[MAX_PKT_BURST]; + int ret, private_ip_idx, public_ip_idx = -1, port_idx; + int new_entry = 0; + uint8_t proto; + uint64_t tsc = rte_rdtsc(); + void *keys[MAX_PKT_BURST]; + int32_t positions[MAX_PKT_BURST]; + int map[MAX_PKT_BURST] = {0}; + + if (unlikely(task->dump_public_hash)) { + const struct public_key *next_key; + void *next_data; + uint32_t iter = 0; + int i = 0; + int ret; + + while ((ret = rte_hash_iterate(task->public_ip_port_hash, (const void **)&next_key, &next_data, &iter)) >= 0) { + plogx_info("Public entry %d (index %d): ip = %d.%d.%d.%d, port = %d ===> private entry: ip = %d.%d.%d.%d, port = %d\n", i++, ret, IP4(next_key->ip_addr), next_key->l4_port, IP4(task->public_entries[ret].ip_addr),task->public_entries[ret].l4_port); + } + task->dump_public_hash = 0; + } + if (unlikely(task->dump_private_hash)) { + const struct private_key *next_key; + void *next_data; + uint32_t iter = 0; + int i = 0; + int ret; + + while ((ret = rte_hash_iterate(task->private_ip_port_hash, (const void **)&next_key, &next_data, &iter)) >= 0) { + plogx_info("Private entry %d (index %d): ip = %d.%d.%d.%d, port = %d ===> public entry: ip = %d.%d.%d.%d, port = %d\n", i++, ret, IP4(next_key->ip_addr), next_key->l4_port, IP4(task->private_flow_entries[ret].ip_addr),task->private_flow_entries[ret].l4_port); + } + task->dump_private_hash = 0; + } + + for (j = 0; j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 0; j < n_pkts; ++j) { + pkt[j] = rte_pktmbuf_mtod(mbufs[j], struct pkt_eth_ipv4 *); + PREFETCH0(pkt[j]); + } + if (task->private) { + struct private_key key[MAX_PKT_BURST]; + for (j = 0; j < n_pkts; ++j) { + /* Currently, only support eth/ipv4 packets */ + if (pkt[j]->ether_hdr.ether_type != ETYPE_IPv4) { + plogx_info("Currently, only support eth/ipv4 packets\n"); + out[j] = OUT_DISCARD; + keys[j] = (void *)NULL; + continue; + } + key[j].ip_addr = pkt[j]->ipv4_hdr.src_addr; + key[j].l4_port = pkt[j]->udp_hdr.src_port; + keys[j] = &key[j]; + } + ret = rte_hash_lookup_bulk(task->private_ip_port_hash, (const void **)&keys, n_pkts, positions); + if (unlikely(ret < 0)) { + plogx_info("lookup_bulk failed in private_ip_port_hash\n"); + return -1; + } + int n_new_mapping = 0; + for (j = 0; j < n_pkts; ++j) { + port_idx = positions[j]; + if (unlikely(port_idx < 0)) { + plogx_dbg("ip %d.%d.%d.%d / port %x not found in private ip/port hash\n", IP4(pkt[j]->ipv4_hdr.src_addr), pkt[j]->udp_hdr.src_port); + map[n_new_mapping] = j; + keys[n_new_mapping++] = (void *)&(pkt[j]->ipv4_hdr.src_addr); + } else { + ip_addr = &(pkt[j]->ipv4_hdr.src_addr); + udp_src_port = &(pkt[j]->udp_hdr.src_port); + plogx_dbg("ip/port %d.%d.%d.%d / %x found in private ip/port hash\n", IP4(pkt[j]->ipv4_hdr.src_addr), pkt[j]->udp_hdr.src_port); + *ip_addr = task->private_flow_entries[port_idx].ip_addr; + *udp_src_port = task->private_flow_entries[port_idx].l4_port; + uint64_t flow_time = task->private_flow_entries[port_idx].flow_time; + if (flow_time + tsc_hz < tsc) { + task->private_flow_entries[port_idx].flow_time = tsc; + } + private_ip_idx = task->private_flow_entries[port_idx].private_ip_idx; + if (task->private_ip_info[private_ip_idx].mac_aging_time + tsc_hz < tsc) + task->private_ip_info[private_ip_idx].mac_aging_time = tsc; + prox_ip_udp_cksum(mbufs[j], &pkt[j]->ipv4_hdr, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + out[j] = route_ipv4(task, mbufs[j]); + } + } + + if (n_new_mapping) { + // Find whether at least IP is already known... + ret = rte_hash_lookup_bulk(task->private_ip_hash, (const void **)&keys, n_new_mapping, positions); + if (unlikely(ret < 0)) { + plogx_info("lookup_bulk failed for private_ip_hash\n"); + for (int k = 0; k < n_new_mapping; ++k) { + j = map[k]; + out[j] = OUT_DISCARD; + } + n_new_mapping = 0; + } + for (int k = 0; k < n_new_mapping; ++k) { + private_ip_idx = positions[k]; + j = map[k]; + ip_addr = &(pkt[j]->ipv4_hdr.src_addr); + proto = pkt[j]->ipv4_hdr.next_proto_id; + udp_src_port = &(pkt[j]->udp_hdr.src_port); + int new_ip_entry = 0; + + if (unlikely(private_ip_idx < 0)) { + private_ip = *ip_addr; + private_port = *udp_src_port; + plogx_dbg("Did not find private ip %d.%d.%d.%d in ip hash table, looking for new public ip\n", IP4(*ip_addr)); + // IP not found, need to get a new IP/port mapping + public_ip_idx = get_new_ip(task, &public_ip); + if (public_ip_idx < 0) { + plogx_info("Unable to find new ip/port\n"); + out[j] = OUT_DISCARD; + continue; + } else { + plogx_dbg("found new public ip %d.%d.%d.%d at public IP index %d\n", IP4(public_ip), public_ip_idx); + } + private_ip_idx = rte_hash_add_key(task->private_ip_hash, (const void *)ip_addr); + // The key might be added multiple time - in case the same key was present in the bulk_lookup multiple times + // As such this is not an issue - the add_key will returns the index as for a new key + // This scenario should not happen often in real time use case + // as a for a new flow (flow renewal), probably only one packet will be sent (e.g. TCP SYN) + if (private_ip_idx < 0) { + release_ip(task, &public_ip, public_ip_idx); + plogx_info("Unable add ip %d.%d.%d.%d in private ip hash\n", IP4(*ip_addr)); + out[j] = OUT_DISCARD; + continue; + } else if (task->private_ip_info[private_ip_idx].public_ip) { + plogx_info("race condition properly handled : ip %d.%d.%d.%d already in private ip hash\n", IP4(*ip_addr)); + release_ip(task, &public_ip, public_ip_idx); + public_ip = task->private_ip_info[private_ip_idx].public_ip; + public_ip_idx = task->private_ip_info[private_ip_idx].public_ip_idx; + } else { + plogx_dbg("Added ip %d.%d.%d.%d in private ip hash\n", IP4(*ip_addr)); + rte_memcpy(&task->private_ip_info[private_ip_idx].private_mac, ((uint8_t *)pkt) + 6, 6); + task->private_ip_info[private_ip_idx].public_ip = public_ip; + task->private_ip_info[private_ip_idx].static_entry = 0; + task->private_ip_info[private_ip_idx].public_ip_idx = public_ip_idx; + new_ip_entry = 1; + } + } else { + public_ip = task->private_ip_info[private_ip_idx].public_ip; + public_ip_idx = task->private_ip_info[private_ip_idx].public_ip_idx; + } + port_idx = add_new_port_entry(task, proto, public_ip_idx, private_ip_idx, *ip_addr, *udp_src_port, mbufs[j], tsc, &public_port); + if (port_idx < 0) { + // TODO: delete IP in ip_hash + if ((new_ip_entry) && (task->last_ip != 0)) { + release_ip(task, &public_ip, public_ip_idx); + task->last_ip--; + } else if (new_ip_entry) { + release_ip(task, &public_ip, public_ip_idx); + task->last_ip = task->public_ip_count-1; + } + plogx_info("Failed to add new port entry\n"); + out[j] = OUT_DISCARD; + continue; + } else { + private_ip = *ip_addr; + private_port = *udp_src_port; + plogx_info("Added new ip/port: private ip/port = %d.%d.%d.%d/%x public ip/port = %d.%d.%d.%d/%x, index = %d\n", IP4(private_ip), private_port, IP4(public_ip), public_port, port_idx); + } + // task->private_flow_entries[port_idx].ip_addr = task->private_ip_info[private_ip_idx].public_ip; + plogx_info("Added new port: private ip/port = %d.%d.%d.%d/%x, public ip/port = %d.%d.%d.%d/%x\n", IP4(private_ip), private_port, IP4(task->private_ip_info[private_ip_idx].public_ip), public_port); + *ip_addr = public_ip ; + *udp_src_port = public_port; + uint64_t flow_time = task->private_flow_entries[port_idx].flow_time; + if (flow_time + tsc_hz < tsc) { + task->private_flow_entries[port_idx].flow_time = tsc; + } + if (task->private_ip_info[private_ip_idx].mac_aging_time + tsc_hz < tsc) + task->private_ip_info[private_ip_idx].mac_aging_time = tsc; + prox_ip_udp_cksum(mbufs[j], &pkt[j]->ipv4_hdr, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + // TODO: if route fails while just added new key in table, should we delete the key from the table? + out[j] = route_ipv4(task, mbufs[j]); + if (out[j] && new_entry) { + delete_port_entry(task, proto, private_ip, private_port, *ip_addr, *udp_src_port, public_ip_idx); + plogx_info("Deleted port: private ip/port = %d.%d.%d.%d/%x, public ip/port = %d.%d.%d.%d/%x\n", IP4(private_ip), private_port, IP4(*ip_addr), *udp_src_port); + } + } + } + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + } else { + struct public_key public_key[MAX_PKT_BURST]; + for (j = 0; j < n_pkts; ++j) { + /* Currently, only support eth/ipv4 packets */ + if (pkt[j]->ether_hdr.ether_type != ETYPE_IPv4) { + plogx_info("Currently, only support eth/ipv4 packets\n"); + out[j] = OUT_DISCARD; + keys[j] = (void *)NULL; + continue; + } + public_key[j].ip_addr = pkt[j]->ipv4_hdr.dst_addr; + public_key[j].l4_port = pkt[j]->udp_hdr.dst_port; + keys[j] = &public_key[j]; + } + ret = rte_hash_lookup_bulk(task->public_ip_port_hash, (const void **)&keys, n_pkts, positions); + if (ret < 0) { + plogx_err("Failed lookup bulk public_ip_port_hash\n"); + return -1; + } + for (j = 0; j < n_pkts; ++j) { + port_idx = positions[j]; + ip_addr = &(pkt[j]->ipv4_hdr.dst_addr); + udp_src_port = &(pkt[j]->udp_hdr.dst_port); + if (port_idx < 0) { + plogx_err("Failed to find ip/port %d.%d.%d.%d/%x in public_ip_port_hash\n", IP4(*ip_addr), *udp_src_port); + out[j] = OUT_DISCARD; + } else { + plogx_dbg("Found ip/port %d.%d.%d.%d/%x in public_ip_port_hash\n", IP4(*ip_addr), *udp_src_port); + *ip_addr = task->public_entries[port_idx].ip_addr; + *udp_src_port = task->public_entries[port_idx].l4_port; + private_ip_idx = task->public_entries[port_idx].private_ip_idx; + plogx_dbg("Found private IP info for ip %d.%d.%d.%d\n", IP4(*ip_addr)); + rte_memcpy(((uint8_t *)(pkt[j])) + 0, &task->private_ip_info[private_ip_idx].private_mac, 6); + rte_memcpy(((uint8_t *)(pkt[j])) + 6, &task->src_mac_from_dpdk_port[task->public_entries[port_idx].dpdk_port], 6); + out[j] = task->public_entries[port_idx].dpdk_port; + } + prox_ip_udp_cksum(mbufs[j], &pkt[j]->ipv4_hdr, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + } + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + } + +} + +static int lua_to_hash_nat(struct task_args *targ, struct lua_State *L, enum lua_place from, const char *name, uint8_t socket) +{ + struct rte_hash *tmp_priv_ip_hash, *tmp_priv_hash, *tmp_pub_hash; + struct private_flow_entry *tmp_priv_flow_entries; + struct public_entry *tmp_pub_entries; + uint32_t n_entries = 0;; + uint32_t ip_from, ip_to; + uint16_t port_from, port_to; + int ret, idx, pop, pop2, pop3, n_static_entries = 0; + uint32_t dst_ip1, dst_ip2; + struct val_range dst_port; + struct public_ip_config_info *ip_info; + struct public_ip_config_info *tmp_public_ip_config_info; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + plogx_err("Can't read cgnat since data is not a table\n"); + return -1; + } + + struct tmp_public_ip { + uint32_t ip_beg; + uint32_t ip_end; + uint16_t port_beg; + uint16_t port_end; + }; + struct tmp_static_ip { + uint32_t private_ip; + uint32_t public_ip; + }; + struct tmp_static_ip_port { + uint32_t private_ip; + uint32_t public_ip; + uint32_t n_ports; + uint16_t private_port; + uint16_t public_port; + int ip_found; + uint8_t port_found; + }; + uint32_t n_public_groups = 0; + uint32_t n_public_ip = 0; + uint32_t n_static_ip = 0; + uint32_t n_static_ip_port = 0; + unsigned int i = 0; + struct tmp_public_ip *tmp_public_ip = NULL; + struct tmp_static_ip *tmp_static_ip = NULL; + struct tmp_static_ip_port *tmp_static_ip_port = NULL; + + // Look for Dynamic entries configuration + plogx_info("Reading dynamic NAT table\n"); + if ((pop2 = lua_getfrom(L, TABLE, "dynamic")) < 0) { + plogx_info("No dynamic table found\n"); + } else { + uint64_t n_ip, n_port; + if (!lua_istable(L, -1)) { + plogx_err("Can't read cgnat since data is not a table\n"); + return -1; + } + lua_len(L, -1); + n_public_groups = lua_tointeger(L, -1); + plogx_info("%d groups of public IP\n", n_public_groups); + tmp_public_ip = (struct tmp_public_ip *)malloc(n_public_groups * sizeof(struct tmp_public_ip)); + PROX_PANIC(tmp_public_ip == NULL, "Failed to allocated tmp_public_ip\n"); + lua_pop(L, 1); + lua_pushnil(L); + + while (lua_next(L, -2)) { + if (lua_to_ip(L, TABLE, "public_ip_range_start", &dst_ip1) || + lua_to_ip(L, TABLE, "public_ip_range_stop", &dst_ip2) || + lua_to_val_range(L, TABLE, "public_port", &dst_port)) + return -1; + PROX_PANIC(dst_ip2 < dst_ip1, "public_ip_range error: %d.%d.%d.%d < %d.%d.%d.%d\n", (dst_ip2 >> 24), (dst_ip2 >> 16) & 0xFF, (dst_ip2 >> 8) & 0xFF, dst_ip2 & 0xFF, dst_ip1 >> 24, (dst_ip1 >> 16) & 0xFF, (dst_ip1 >> 8) & 0xFF, dst_ip1 & 0xFF); + PROX_PANIC(dst_port.end < dst_port.beg, "public_port error: %d < %d\n", dst_port.end, dst_port.beg); + n_ip = dst_ip2 - dst_ip1 + 1; + n_port = dst_port.end - dst_port.beg + 1; + n_public_ip += n_ip; + plogx_info("Found IP from %d.%d.%d.%d to %d.%d.%d.%d and port from %d to %d\n", dst_ip1 >> 24, (dst_ip1 >> 16) & 0xFF, (dst_ip1 >> 8) & 0xFF, dst_ip1 & 0xFF, (dst_ip2 >> 24), (dst_ip2 >> 16) & 0xFF, (dst_ip2 >> 8) & 0xFF, dst_ip2 & 0xFF, dst_port.beg, dst_port.end); + tmp_public_ip[i].ip_beg = dst_ip1; + tmp_public_ip[i].ip_end = dst_ip2; + tmp_public_ip[i].port_beg = dst_port.beg; + tmp_public_ip[i++].port_end = dst_port.end; + n_entries += n_ip * n_port; + lua_pop(L, 1); + } + lua_pop(L, pop2); + + } + i = 0; + if ((pop2 = lua_getfrom(L, TABLE, "static_ip")) < 0) { + plogx_info("No static ip table found\n"); + } else { + if (!lua_istable(L, -1)) { + plogx_err("Can't read cgnat since data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_static_ip = lua_tointeger(L, -1); + plogx_info("%d entries in static ip table\n", n_static_ip); + lua_pop(L, 1); + tmp_static_ip = (struct tmp_static_ip *)malloc(n_static_ip * sizeof(struct tmp_static_ip)); + PROX_PANIC(tmp_static_ip == NULL, "Failed to allocated tmp_static_ip\n"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_ip(L, TABLE, "src_ip", &ip_from) || + lua_to_ip(L, TABLE, "dst_ip", &ip_to)) + return -1; + ip_from = rte_bswap32(ip_from); + ip_to = rte_bswap32(ip_to); + tmp_static_ip[i].private_ip = ip_from; + tmp_static_ip[i++].public_ip = ip_to; + for (unsigned int j = 0; j < n_public_groups; j++) { + if ((tmp_public_ip[j].ip_beg <= ip_to) && (ip_to <= tmp_public_ip[j].ip_end)) { + PROX_PANIC(1, "list of static ip mapping overlap with list of dynamic IP => not supported yet\n"); + } + } + n_public_ip++; + lua_pop(L, 1); + } + lua_pop(L, pop2); + } + + i = 0; + if ((pop2 = lua_getfrom(L, TABLE, "static_ip_port")) < 0) { + plogx_info("No static table found\n"); + } else { + if (!lua_istable(L, -1)) { + plogx_err("Can't read cgnat since data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_static_ip_port = lua_tointeger(L, -1); + plogx_info("%d entries in static table\n", n_static_ip_port); + lua_pop(L, 1); + tmp_static_ip_port = (struct tmp_static_ip_port *)malloc(n_static_ip_port * sizeof(struct tmp_static_ip_port)); + PROX_PANIC(tmp_static_ip_port == NULL, "Failed to allocated tmp_static_ip_port\n"); + lua_pushnil(L); + + while (lua_next(L, -2)) { + if (lua_to_ip(L, TABLE, "src_ip", &ip_from) || + lua_to_ip(L, TABLE, "dst_ip", &ip_to) || + lua_to_port(L, TABLE, "src_port", &port_from) || + lua_to_port(L, TABLE, "dst_port", &port_to)) + return -1; + + ip_from = rte_bswap32(ip_from); + ip_to = rte_bswap32(ip_to); + port_from = rte_bswap16(port_from); + port_to = rte_bswap16(port_to); + tmp_static_ip_port[i].private_ip = ip_from; + tmp_static_ip_port[i].public_ip = ip_to; + tmp_static_ip_port[i].private_port = port_from; + tmp_static_ip_port[i].public_port = port_to; + tmp_static_ip_port[i].n_ports = 1; + for (unsigned int j = 0; j < n_public_groups; j++) { + if ((tmp_public_ip[j].ip_beg <= rte_bswap32(ip_to)) && (rte_bswap32(ip_to) <= tmp_public_ip[j].ip_end)) { + tmp_static_ip_port[i].ip_found = j + 11; + PROX_PANIC(1, "list of static ip/port mapping overlap with list of dynamic IP => not supported yet\n"); + } + } + for (unsigned int j = 0; j < n_static_ip; j++) { + if ((tmp_static_ip[j].public_ip == ip_to) ) { + tmp_static_ip_port[i].ip_found = j + 1; + PROX_PANIC(1, "list of static ip/port mapping overlap with list of static ip => not supported yet\n"); + } + } + for (unsigned int j = 0; j <= i; j++) { + if (ip_to == tmp_static_ip_port[j].public_ip) { + tmp_static_ip_port[i].ip_found = j + 1; + tmp_static_ip_port[j].n_ports++; + tmp_static_ip_port[i].n_ports = 0; + } + } + i++; + if (!tmp_static_ip_port[i].ip_found) { + n_public_ip++; + n_entries++; + } + lua_pop(L, 1); + } + lua_pop(L, pop2); + } + lua_pop(L, pop); + + tmp_public_ip_config_info = (struct public_ip_config_info *)prox_zmalloc(n_public_ip * sizeof(struct public_ip_config_info), socket); + PROX_PANIC(tmp_public_ip_config_info == NULL, "Failed to allocate PUBLIC IP INFO\n"); + plogx_info("%d PUBLIC IP INFO allocated\n", n_public_ip); + + struct private_ip_info *tmp_priv_ip_info = (struct private_ip_info *)prox_zmalloc(4 * n_public_ip * sizeof(struct public_ip_config_info), socket); + PROX_PANIC(tmp_priv_ip_info == NULL, "Failed to allocate PRIVATE IP INFO\n"); + plogx_info("%d PRIVATE IP INFO allocated\n", 4 * n_public_ip); + + uint32_t ip_free_count = 0; + for (i = 0; i < n_public_groups; i++) { + for (uint32_t ip = tmp_public_ip[i].ip_beg; ip <= tmp_public_ip[i].ip_end; ip++) { + ip_info = &tmp_public_ip_config_info[ip_free_count]; + ip_info->public_ip = rte_bswap32(ip); + ip_info->port_list = (uint16_t *)prox_zmalloc((dst_port.end - dst_port.beg) * sizeof(uint16_t), socket); + PROX_PANIC(ip_info->port_list == NULL, "Failed to allocate list of ports for ip %x\n", ip); + for (uint32_t port = tmp_public_ip[i].port_beg; port <= tmp_public_ip[i].port_end; port++) { + ip_info->port_list[ip_info->port_free_count] = rte_bswap16(port); + ip_info->port_free_count++; + } + ip_info->max_port_count = ip_info->port_free_count; + plogx_dbg("Added IP %d.%d.%d.%d with ports from %x to %x at index %x\n", IP4(ip_info->public_ip), tmp_public_ip[i].port_beg, tmp_public_ip[i].port_end, ip_free_count); + ip_free_count++; + } + } + uint32_t public_ip_count = ip_free_count; + for (i = 0; i < n_static_ip; i++) { + ip_info = &tmp_public_ip_config_info[ip_free_count]; + ip_info->public_ip = tmp_static_ip[i].public_ip; + ip_info->port_list = NULL; + ip_info->max_port_count = 0; + ip_free_count++; + } + for (i = 0; i < n_static_ip_port; i++) { + if (!tmp_static_ip_port[i].ip_found) { + ip_info = &tmp_public_ip_config_info[ip_free_count]; + ip_info->public_ip = tmp_static_ip_port[i].public_ip; + ip_info->port_list = (uint16_t *)prox_zmalloc(tmp_static_ip_port[i].n_ports * sizeof(uint16_t), socket); + PROX_PANIC(ip_info->port_list == NULL, "Failed to allocate list of ports for ip %x\n", tmp_static_ip_port[i].public_ip); + ip_info->port_list[ip_info->port_free_count] = tmp_static_ip_port[i].public_port; + ip_info->port_free_count++; + ip_info->max_port_count = ip_info->port_free_count; + ip_free_count++; + } else { + for (unsigned j = 0; j < ip_free_count; j++) { + ip_info = &tmp_public_ip_config_info[j]; + if (ip_info->public_ip == tmp_static_ip_port[i].public_ip) { + ip_info = &tmp_public_ip_config_info[j]; + ip_info->port_list[ip_info->port_free_count] = tmp_static_ip_port[i].public_port; + ip_info->port_free_count++; + ip_info->max_port_count = ip_info->port_free_count; + break; + } + } + } + } + plogx_info("%d entries in dynamic table\n", n_entries); + + n_entries = n_entries * 4; + static char hash_name[30]; + sprintf(hash_name, "A%03d_hash_nat_table", targ->lconf->id); + struct rte_hash_parameters hash_params = { + .name = hash_name, + .entries = n_entries, + .key_len = sizeof(struct private_key), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + }; + plogx_info("hash table name = %s\n", hash_params.name); + struct private_key private_key; + struct public_key public_key; + tmp_priv_hash = rte_hash_create(&hash_params); + PROX_PANIC(tmp_priv_hash == NULL, "Failed to set up private hash table for NAT\n"); + plogx_info("private hash table allocated, with %d entries of size %d\n", hash_params.entries, hash_params.key_len); + + tmp_priv_flow_entries = (struct private_flow_entry *)prox_zmalloc(n_entries * sizeof(struct private_flow_entry), socket); + PROX_PANIC(tmp_priv_flow_entries == NULL, "Failed to allocate memory for private NAT %u entries\n", n_entries); + plogx_info("private data allocated, with %d entries of size %ld\n", n_entries, sizeof(struct private_flow_entry)); + + hash_name[0]++; + //hash_params.name[0]++; + plogx_info("hash table name = %s\n", hash_params.name); + hash_params.key_len = sizeof(uint32_t); + hash_params.entries = 4 * ip_free_count; + tmp_priv_ip_hash = rte_hash_create(&hash_params); + PROX_PANIC(tmp_priv_ip_hash == NULL, "Failed to set up private ip hash table for NAT\n"); + plogx_info("private ip hash table allocated, with %d entries of size %d\n", hash_params.entries, hash_params.key_len); + + hash_name[0]++; + //hash_params.name[0]++; + plogx_info("hash table name = %s\n", hash_params.name); + hash_params.entries = n_entries; + hash_params.key_len = sizeof(struct public_key), + tmp_pub_hash = rte_hash_create(&hash_params); + PROX_PANIC(tmp_pub_hash == NULL, "Failed to set up public hash table for NAT\n"); + plogx_info("public hash table allocated, with %d entries of size %d\n", hash_params.entries, hash_params.key_len); + + hash_name[0]++; + //hash_params.name[0]++; + tmp_pub_entries = (struct public_entry *)prox_zmalloc(n_entries * sizeof(struct public_entry), socket); + PROX_PANIC(tmp_pub_entries == NULL, "Failed to allocate memory for public NAT %u entries\n", n_entries); + plogx_info("public data allocated, with %d entries of size %ld\n", n_entries, sizeof(struct private_flow_entry)); + + for (i = 0; i < n_static_ip_port; i++) { + ip_to = tmp_static_ip_port[i].public_ip; + ip_from = tmp_static_ip_port[i].private_ip; + port_to = tmp_static_ip_port[i].public_port; + port_from = tmp_static_ip_port[i].private_port; + private_key.ip_addr = ip_from; + private_key.l4_port = port_from; + ret = rte_hash_lookup(tmp_priv_hash, (const void *)&private_key); + PROX_PANIC(ret >= 0, "Key %x %x already exists in NAT private hash table\n", ip_from, port_from); + + idx = rte_hash_add_key(tmp_priv_ip_hash, (const void *)&ip_from); + PROX_PANIC(idx < 0, "Failed to add ip %x to NAT private hash table\n", ip_from); + ret = rte_hash_add_key(tmp_priv_hash, (const void *)&private_key); + PROX_PANIC(ret < 0, "Failed to add Key %x %x to NAT private hash table\n", ip_from, port_from); + tmp_priv_flow_entries[ret].ip_addr = ip_to; + tmp_priv_flow_entries[ret].flow_time = -1; + tmp_priv_flow_entries[ret].private_ip_idx = idx; + tmp_priv_flow_entries[ret].l4_port = port_to; + + public_key.ip_addr = ip_to; + public_key.l4_port = port_to; + ret = rte_hash_lookup(tmp_pub_hash, (const void *)&public_key); + PROX_PANIC(ret >= 0, "Key %d.%d.%d.%d port %x (for private IP %d.%d.%d.%d port %x) already exists in NAT public hash table fir IP %d.%d.%d.%d port %x\n", IP4(ip_to), port_to, IP4(ip_from), port_from, IP4(tmp_pub_entries[ret].ip_addr), tmp_pub_entries[ret].l4_port); + + ret = rte_hash_add_key(tmp_pub_hash, (const void *)&public_key); + PROX_PANIC(ret < 0, "Failed to add Key %x %x to NAT public hash table\n", ip_to, port_to); + tmp_pub_entries[ret].ip_addr = ip_from; + tmp_pub_entries[ret].l4_port = port_from; + tmp_pub_entries[ret].private_ip_idx = idx; + } + + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + struct task_args *target_targ = (struct task_args *)&(targ->lconf->targs[task_id]); + enum task_mode smode = target_targ->mode; + if (CGNAT == smode) { + target_targ->public_ip_count = public_ip_count; + target_targ->private_ip_hash = tmp_priv_ip_hash; + target_targ->private_ip_port_hash = tmp_priv_hash; + target_targ->private_ip_info = tmp_priv_ip_info; + target_targ->private_flow_entries = tmp_priv_flow_entries; + target_targ->public_ip_port_hash = tmp_pub_hash; + target_targ->public_entries = tmp_pub_entries; + target_targ->public_ip_config_info = tmp_public_ip_config_info; + } + } + return 0; +} + +static void early_init_task_nat(struct task_args *targ) +{ + int ret; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + if (!targ->private_ip_hash) { + ret = lua_to_hash_nat(targ, prox_lua(), GLOBAL, targ->nat_table, socket_id); + PROX_PANIC(ret != 0, "Failed to load NAT table from lua:\n%s\n", get_lua_to_errors()); + } +} + +static void init_task_nat(struct task_base *tbase, struct task_args *targ) +{ + struct task_nat *task = (struct task_nat *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + /* Use destination IP by default. */ + task->private = targ->use_src; + + PROX_PANIC(!strcmp(targ->nat_table, ""), "No nat table specified\n"); + task->lconf = targ->lconf; + task->runtime_flags = targ->runtime_flags; + + task->public_ip_count = targ->public_ip_count; + task->last_ip = targ->public_ip_count; + task->private_ip_hash = targ->private_ip_hash; + task->private_ip_port_hash = targ->private_ip_port_hash; + task->private_ip_info = targ->private_ip_info; + task->private_flow_entries = targ->private_flow_entries; + task->public_ip_port_hash = targ->public_ip_port_hash; + task->public_entries = targ->public_entries; + task->public_ip_config_info = targ->public_ip_config_info; + + proto_ipsrc_portsrc_mask = _mm_set_epi32(BIT_0_TO_15, 0, ALL_32_BITS, BIT_8_TO_15); + proto_ipdst_portdst_mask = _mm_set_epi32(BIT_16_TO_31, ALL_32_BITS, 0, BIT_8_TO_15); + + struct lpm4 *lpm; + + PROX_PANIC(!strcmp(targ->route_table, ""), "route table not specified\n"); + if (targ->flags & TASK_ARG_LOCAL_LPM) { + int ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm); + PROX_PANIC(ret, "Failed to load IPv4 LPM:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->route_table, lpm); + task->number_free_rules = lpm->n_free_rules; + } else { + lpm = prox_sh_find_socket(socket_id, targ->route_table); + if (!lpm) { + int ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm); + PROX_PANIC(ret, "Failed to load IPv4 LPM:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->route_table, lpm); + } + } + task->ipv4_lpm = lpm->rte_lpm; + task->next_hops = lpm->next_hops; + task->number_free_rules = lpm->n_free_rules; + + for (uint32_t i = 0; i < MAX_HOP_INDEX; i++) { + int tx_port = task->next_hops[i].mac_port.out_idx; + if ((tx_port > targ->nb_txports - 1) && (tx_port > targ->nb_txrings - 1)) { + PROX_PANIC(1, "Routing Table contains port %d but only %d tx port/ %d ring:\n", tx_port, targ->nb_txports, targ->nb_txrings); + } + } + + if (targ->nb_txrings) { + struct task_args *dtarg; + struct core_task ct; + for (uint32_t i = 0; i < targ->nb_txrings; ++i) { + ct = targ->core_task_set[0].core_task[i]; + dtarg = core_targ_get(ct.core, ct.task); + dtarg = find_reachable_task_sending_to_port(dtarg); + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[dtarg->tx_port_queue[0].port].eth_addr))) | ((uint64_t)ETYPE_IPv4 << (64 - 16)); + task->src_mac_from_dpdk_port[dtarg->tx_port_queue[0].port] = task->src_mac[i]; + plogx_dbg("src_mac = %lx for port %d %d\n", task->src_mac[i], i, dtarg->tx_port_queue[0].port); + } + } else { + for (uint32_t i = 0; i < targ->nb_txports; ++i) { + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[targ->tx_port_queue[i].port].eth_addr))) | ((uint64_t)ETYPE_IPv4 << (64 - 16)); + task->src_mac_from_dpdk_port[targ->tx_port_queue[0].port] = task->src_mac[i]; + plogx_dbg("src_mac = %lx for port %d %d\n", task->src_mac[i], i, targ->tx_port_queue[i].port); + } + } + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } +} + +/* Basic static nat. */ +static struct task_init task_init_nat = { + .mode = CGNAT, + .mode_str = "cgnat", + .early_init = early_init_task_nat, + .init = init_task_nat, + .handle = handle_nat_bulk, +#ifdef SOFT_CRC + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_ROUTING|TASK_FEATURE_ZERO_RX, +#else + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_ROUTING|TASK_FEATURE_ZERO_RX, +#endif + .size = sizeof(struct task_nat), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_nat(void) +{ + reg_task(&task_init_nat); +} diff --git a/VNFs/DPPD-PROX/handle_cgnat.h b/VNFs/DPPD-PROX/handle_cgnat.h new file mode 100644 index 00000000..ab26be34 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_cgnat.h @@ -0,0 +1,25 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_CGNAT_H_ +#define _HANDLE_CGNAT_H_ + +struct task_nat; + +void task_cgnat_dump_public_hash(struct task_nat *task); +void task_cgnat_dump_private_hash(struct task_nat *task); + +#endif diff --git a/VNFs/DPPD-PROX/handle_classify.c b/VNFs/DPPD-PROX/handle_classify.c new file mode 100644 index 00000000..f4f96aaf --- /dev/null +++ b/VNFs/DPPD-PROX/handle_classify.c @@ -0,0 +1,133 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> +#include <stdio.h> +#include <string.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "lconf.h" +#include "task_base.h" +#include "task_init.h" +#include "defines.h" +#include "prefetch.h" +#include "qinq.h" +#include "prox_cfg.h" +#include "log.h" +#include "quit.h" +#include "prox_shared.h" + +struct task_classify { + struct task_base base; + uint16_t *user_table; + uint8_t *dscp; +}; + +static inline void handle_classify(struct task_classify *task, struct rte_mbuf *mbuf) +{ + const struct qinq_hdr *pqinq = rte_pktmbuf_mtod(mbuf, const struct qinq_hdr *); + + uint32_t qinq = PKT_TO_LUTQINQ(pqinq->svlan.vlan_tci, pqinq->cvlan.vlan_tci); + + /* Traffic class can be set by ACL task. If this is the case, + don't overwrite it using dscp. Instead, use the + traffic class that had been set. */ + + uint32_t prev_tc; +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + uint32_t dummy; + rte_sched_port_pkt_read_tree_path(mbuf, &dummy, &dummy, &prev_tc, &dummy); +#else + struct rte_sched_port_hierarchy *sched = (struct rte_sched_port_hierarchy *) &mbuf->pkt.hash.sched; + prev_tc = sched->traffic_class; +#endif + + const struct ipv4_hdr *ipv4_hdr = (const struct ipv4_hdr *)(pqinq + 1); + uint8_t dscp = task->dscp[ipv4_hdr->type_of_service >> 2]; + + uint8_t queue = dscp & 0x3; + uint8_t tc = prev_tc? prev_tc : dscp >> 2; + + rte_sched_port_pkt_write(mbuf, 0, task->user_table[qinq], tc, queue, 0); +} + +static int handle_classify_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_classify *task = (struct task_classify *)tbase; + + uint16_t j; +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(mbufs[j + PREFETCH_OFFSET]); + prefetch_nta(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + handle_classify(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + handle_classify(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); +} + +static void init_task_classify(struct task_base *tbase, struct task_args *targ) +{ + struct task_classify *task = (struct task_classify *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->user_table = prox_sh_find_socket(socket_id, "user_table"); + if (!task->user_table) { + PROX_PANIC(!strcmp(targ->user_table, ""), "No user table defined\n"); + int ret = lua_to_user_table(prox_lua(), GLOBAL, targ->user_table, socket_id, &task->user_table); + PROX_PANIC(ret, "Failed to create user table from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, "user_table", task->user_table); + } + + PROX_PANIC(!strcmp(targ->dscp, ""), "DSCP table not specified\n"); + task->dscp = prox_sh_find_socket(socket_id, targ->dscp); + if (!task->dscp) { + int ret = lua_to_dscp(prox_lua(), GLOBAL, targ->dscp, socket_id, &task->dscp); + PROX_PANIC(ret, "Failed to create dscp table from config\n"); + prox_sh_add_socket(socket_id, targ->dscp, task->dscp); + } +} + +static struct task_init task_init_classify = { + .mode_str = "classify", + .init = init_task_classify, + .handle = handle_classify_bulk, + .flag_features = TASK_FEATURE_NEVER_DISCARDS, + .size = sizeof(struct task_classify) +}; + +__attribute__((constructor)) static void reg_task_classify(void) +{ + reg_task(&task_init_classify); +} diff --git a/VNFs/DPPD-PROX/handle_dump.c b/VNFs/DPPD-PROX/handle_dump.c new file mode 100644 index 00000000..c35a6e9e --- /dev/null +++ b/VNFs/DPPD-PROX/handle_dump.c @@ -0,0 +1,131 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <pcap.h> + +#include "prox_malloc.h" +#include "clock.h" +#include "log.h" +#include "lconf.h" +#include "task_init.h" +#include "task_base.h" +#include "stats.h" + +struct task_dump { + struct task_base base; + uint32_t n_mbufs; + struct rte_mbuf **mbufs; + uint32_t n_pkts; + char pcap_file[128]; +}; + +static uint16_t buffer_packets(struct task_dump *task, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + uint16_t j = 0; + + if (task->n_mbufs == task->n_pkts) + return 0; + + for (j = 0; j < n_pkts && task->n_mbufs < task->n_pkts; ++j) { + mbufs[j]->udata64 = rte_rdtsc(); + task->mbufs[task->n_mbufs++] = mbufs[j]; + } + + return j; +} + +static int handle_dump_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_dump *task = (struct task_dump *)tbase; + const uint16_t ofs = buffer_packets(task, mbufs, n_pkts); + + for (uint16_t j = ofs; j < n_pkts; ++j) + rte_pktmbuf_free(mbufs[j]); + TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, n_pkts - ofs); + return n_pkts; +} + +static void init_task_dump(struct task_base *tbase, __attribute__((unused)) struct task_args *targ) +{ + struct task_dump *task = (struct task_dump *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->mbufs = prox_zmalloc(sizeof(*task->mbufs) * targ->n_pkts, socket_id); + task->n_pkts = targ->n_pkts; + if (!strcmp(targ->pcap_file, "")) { + strcpy(targ->pcap_file, "out.pcap"); + } + strncpy(task->pcap_file, targ->pcap_file, sizeof(task->pcap_file)); +} + +static void stop(struct task_base *tbase) +{ + struct task_dump *task = (struct task_dump *)tbase; + static pcap_dumper_t *pcap_dump_handle; + pcap_t *handle; + uint32_t n_pkts = 65536; + struct pcap_pkthdr header = {{0}, 0, 0}; + static int once = 0; + char err_str[PCAP_ERRBUF_SIZE]; + const uint64_t hz = rte_get_tsc_hz(); + struct timeval tv = {0}; + uint64_t tsc, beg = 0; + + plogx_info("Dumping %d packets to '%s'\n", task->n_mbufs, task->pcap_file); + handle = pcap_open_dead(DLT_EN10MB, n_pkts); + pcap_dump_handle = pcap_dump_open(handle, task->pcap_file); + + if (task->n_mbufs) { + beg = task->mbufs[0]->udata64; + } + for (uint32_t j = 0; j < task->n_mbufs; ++j) { + tsc = task->mbufs[j]->udata64 - beg; + header.len = rte_pktmbuf_pkt_len(task->mbufs[j]); + header.caplen = header.len; + tsc_to_tv(&header.ts, tsc); + pcap_dump((unsigned char *)pcap_dump_handle, &header, rte_pktmbuf_mtod(task->mbufs[j], void *)); + } + + pcap_dump_close(pcap_dump_handle); + pcap_close(handle); + plogx_info("Dump complete, releasing mbufs\n"); + + uint32_t j = 0; + + while (j + 64 < task->n_mbufs) { + tbase->tx_pkt(tbase, &task->mbufs[j], 64, NULL); + j += 64; + } + if (j < task->n_mbufs) { + tbase->tx_pkt(tbase, &task->mbufs[j], task->n_mbufs - j, NULL); + } + task->n_mbufs = 0; +} + +static struct task_init task_init_dump = { + .mode_str = "dump", + .init = init_task_dump, + .handle = handle_dump_bulk, + .stop = stop, + .flag_features = TASK_FEATURE_ZERO_RX, + .size = sizeof(struct task_dump) +}; + +__attribute__((constructor)) static void reg_task_dump(void) +{ + reg_task(&task_init_dump); +} diff --git a/VNFs/DPPD-PROX/handle_fm.c b/VNFs/DPPD-PROX/handle_fm.c new file mode 100644 index 00000000..c4a10e67 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_fm.c @@ -0,0 +1,373 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <dlfcn.h> + +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_tcp.h> +#include <rte_cycles.h> +#include <rte_ether.h> +#include <rte_eth_ctrl.h> + +#include "log.h" +#include "quit.h" +#include "lconf.h" +#include "task_init.h" +#include "task_base.h" +#include "kv_store_expire.h" +#include "stats.h" +#include "prox_shared.h" +#include "etypes.h" +#include "prox_cfg.h" +#include "dpi/dpi.h" + +struct task_dpi_per_core { + void *dpi_opaque; +}; + +struct task_fm { + struct task_base base; + /* FM related fields */ + struct kv_store_expire *kv_store_expire; + void *dpi_opaque; + + struct dpi_engine dpi_engine; + struct task_dpi_per_core *dpi_shared; /* Used only during init */ +}; + +struct eth_ip4_udp { + struct ether_hdr l2; + struct ipv4_hdr l3; + union { + struct udp_hdr udp; + struct tcp_hdr tcp; + } l4; +} __attribute__((packed)); + +union pkt_type { + struct { + uint16_t etype; + uint8_t ip_byte; + uint8_t next_proto; + } __attribute__((packed)); + uint32_t val; +}; + +static union pkt_type pkt_type_udp = { + .next_proto = IPPROTO_UDP, + .ip_byte = 0x45, + .etype = ETYPE_IPv4, +}; + +static union pkt_type pkt_type_tcp = { + .next_proto = IPPROTO_TCP, + .ip_byte = 0x45, + .etype = ETYPE_IPv4, +}; + +static int extract_flow_info(struct eth_ip4_udp *p, struct flow_info *fi, struct flow_info *fi_flipped, uint32_t *len, uint8_t **payload) +{ + union pkt_type pkt_type = { + .next_proto = p->l3.next_proto_id, + .ip_byte = p->l3.version_ihl, + .etype = p->l2.ether_type, + }; + + memset(fi->reservered, 0, sizeof(fi->reservered)); + memset(fi_flipped->reservered, 0, sizeof(fi_flipped->reservered)); + + if (pkt_type.val == pkt_type_udp.val) { + fi->ip_src = p->l3.src_addr; + fi->ip_dst = p->l3.dst_addr; + fi->ip_proto = p->l3.next_proto_id; + fi->port_src = p->l4.udp.src_port; + fi->port_dst = p->l4.udp.dst_port; + + fi_flipped->ip_src = p->l3.dst_addr; + fi_flipped->ip_dst = p->l3.src_addr; + fi_flipped->ip_proto = p->l3.next_proto_id; + fi_flipped->port_src = p->l4.udp.dst_port; + fi_flipped->port_dst = p->l4.udp.src_port; + + *len = rte_be_to_cpu_16(p->l4.udp.dgram_len) - sizeof(struct udp_hdr); + *payload = (uint8_t*)(&p->l4.udp) + sizeof(struct udp_hdr); + return 0; + } + else if (pkt_type.val == pkt_type_tcp.val) { + fi->ip_src = p->l3.src_addr; + fi->ip_dst = p->l3.dst_addr; + fi->ip_proto = p->l3.next_proto_id; + fi->port_src = p->l4.tcp.src_port; + fi->port_dst = p->l4.tcp.dst_port; + + fi_flipped->ip_src = p->l3.dst_addr; + fi_flipped->ip_dst = p->l3.src_addr; + fi_flipped->ip_proto = p->l3.next_proto_id; + fi_flipped->port_src = p->l4.tcp.dst_port; + fi_flipped->port_dst = p->l4.tcp.src_port; + + *len = rte_be_to_cpu_16(p->l3.total_length) - sizeof(struct ipv4_hdr) - ((p->l4.tcp.data_off >> 4)*4); + *payload = ((uint8_t*)&p->l4.tcp) + ((p->l4.tcp.data_off >> 4)*4); + return 0; + } + + return -1; +} + +static int is_flow_beg(const struct flow_info *fi, const struct eth_ip4_udp *p) +{ + return fi->ip_proto == IPPROTO_UDP || + (fi->ip_proto == IPPROTO_TCP && p->l4.tcp.tcp_flags & TCP_SYN_FLAG); +} + +static void *lookup_flow(struct task_fm *task, struct flow_info *fi, uint64_t now_tsc) +{ + struct kv_store_expire_entry *entry; + + entry = kv_store_expire_get(task->kv_store_expire, fi, now_tsc); + + return entry ? entry_value(task->kv_store_expire, entry) : NULL; +} + +static void *lookup_or_insert_flow(struct task_fm *task, struct flow_info *fi, uint64_t now_tsc) +{ + struct kv_store_expire_entry *entry; + + entry = kv_store_expire_get_or_put(task->kv_store_expire, fi, now_tsc); + + return entry ? entry_value(task->kv_store_expire, entry) : NULL; +} + +static int handle_fm(struct task_fm *task, struct rte_mbuf *mbuf, uint64_t now_tsc) +{ + struct eth_ip4_udp *p; + struct flow_info fi, fi_flipped; + void *flow_data; + uint32_t len; + uint8_t *payload; + uint32_t res[2]; + size_t res_len = 2; + int flow_beg; + struct dpi_payload dpi_payload; + int is_upstream = 0; + + p = rte_pktmbuf_mtod(mbuf, struct eth_ip4_udp *); + + if (0 != extract_flow_info(p, &fi, &fi_flipped, &len, &payload)) { + plogx_err("Unknown packet type\n"); + return OUT_DISCARD; + } + + /* First, try to see if the flow already exists where the + current packet is sent by the server. */ + if (!(flow_data = lookup_flow(task, &fi_flipped, now_tsc))) { + /* Insert a new flow, only if this is the first packet + in the flow. */ + is_upstream = 1; + if (is_flow_beg(&fi, p)) + flow_data = lookup_or_insert_flow(task, &fi, now_tsc); + else + flow_data = lookup_flow(task, &fi, now_tsc); + } + + if (!flow_data) + return OUT_DISCARD; + else if (!len) + return 0; + + dpi_payload.payload = payload; + dpi_payload.len = len; + dpi_payload.client_to_server = is_upstream; + gettimeofday(&dpi_payload.tv, NULL); + task->dpi_engine.dpi_process(task->dpi_opaque, is_upstream? &fi : &fi_flipped, flow_data, &dpi_payload, res, &res_len); + return OUT_HANDLED; +} + +static int handle_fm_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_fm *task = (struct task_fm *)tbase; + uint64_t now_tsc = rte_rdtsc(); + uint16_t handled = 0; + uint16_t discard = 0; + int ret; + + for (uint16_t i = 0; i < n_pkts; ++i) { + ret = handle_fm(task, mbufs[i], now_tsc); + if (ret == OUT_DISCARD) + discard++; + else if (ret == OUT_HANDLED) + handled++; + } + + for (uint16_t i = 0; i < n_pkts; ++i) + rte_pktmbuf_free(mbufs[i]); + + TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, handled); + TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, discard); + return 0; +} + +static void load_dpi_engine(const char *dpi_engine_path, struct dpi_engine *dst) +{ + void *handle = prox_sh_find_system(dpi_engine_path); + + if (handle == NULL) { + plogx_info("Loading DPI engine from '%s'\n", dpi_engine_path); + handle = dlopen(dpi_engine_path, RTLD_NOW | RTLD_GLOBAL); + + PROX_PANIC(handle == NULL, "Failed to load dpi engine from '%s' with error:\n\t\t%s\n", dpi_engine_path, dlerror()); + prox_sh_add_system(dpi_engine_path, handle); + } + + struct dpi_engine *(*get_dpi_engine)(void) = dlsym(handle, "get_dpi_engine"); + + PROX_PANIC(get_dpi_engine == NULL, "Failed to find get_dpi_engine function from '%s'\n", dpi_engine_path); + struct dpi_engine *dpi_engine = get_dpi_engine(); + + dpi_engine->dpi_print = plog_info; + rte_memcpy(dst, dpi_engine, sizeof(*dst)); +} + +static uint32_t count_fm_cores(void) +{ + uint32_t n_cores = 0; + uint32_t lcore_id = -1; + struct lcore_cfg *lconf; + + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + if (!strcmp(lconf->targs[task_id].task_init->mode_str, "fm")) { + n_cores++; + /* Only intersted in number of cores + so break here. */ + break; + } + } + } + + return n_cores; +} + +static struct kv_store_expire *get_shared_flow_table(struct task_args *targ, struct dpi_engine *de) +{ + struct kv_store_expire *ret = prox_sh_find_core(targ->lconf->id, "flow_table"); + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + if (!ret) { + ret = kv_store_expire_create(rte_align32pow2(targ->flow_table_size) * 4, + sizeof(struct flow_info), + de->dpi_get_flow_entry_size(), + socket_id, + de->dpi_flow_expire, + rte_get_tsc_hz() * 60); + PROX_PANIC(ret == NULL, "Failed to allocate KV store\n"); + prox_sh_add_core(targ->lconf->id, "flow_table", ret); + } + return ret; +} + +static struct task_dpi_per_core *get_shared_dpi_shared(struct task_args *targ) +{ + static const char *name = "dpi_shared"; + struct task_dpi_per_core *ret = prox_sh_find_core(targ->lconf->id, name); + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + if (!ret) { + ret = prox_zmalloc(sizeof(*ret), socket_id); + prox_sh_add_core(targ->lconf->id, name, ret); + } + return ret; +} + +static void init_task_fm(struct task_base *tbase, struct task_args *targ) +{ + struct task_fm *task = (struct task_fm *)tbase; + static int dpi_inited = 0; + + load_dpi_engine(targ->dpi_engine_path, &task->dpi_engine); + + task->kv_store_expire = get_shared_flow_table(targ, &task->dpi_engine); + task->dpi_shared = get_shared_dpi_shared(targ); + + if (!dpi_inited) { + uint32_t n_threads = count_fm_cores(); + const char *dpi_params[16]; + + plogx_info("Initializing DPI with %u threads\n", n_threads); + dpi_inited = 1; + + PROX_PANIC(targ->n_dpi_engine_args > 16, "Too many DPI arguments"); + for (size_t i = 0; i < targ->n_dpi_engine_args && i < 16; ++i) + dpi_params[i] = targ->dpi_engine_args[i]; + + int ret = task->dpi_engine.dpi_init(n_threads, targ->n_dpi_engine_args, dpi_params); + + PROX_PANIC(ret, "Failed to initialize DPI engine\n"); + } +} + +static void start_first(struct task_base *tbase) +{ + struct task_fm *task = (struct task_fm *)tbase; + void *ret = task->dpi_engine.dpi_thread_start(); + + task->dpi_shared->dpi_opaque = ret; + PROX_PANIC(ret == NULL, "dpi_thread_init failed\n"); +} + +static void start(struct task_base *tbase) +{ + struct task_fm *task = (struct task_fm *)tbase; + + task->dpi_opaque = task->dpi_shared->dpi_opaque; + PROX_PANIC(task->dpi_opaque == NULL, "dpi_opaque == NULL"); +} + +static void stop(struct task_base *tbase) +{ + struct task_fm *task = (struct task_fm *)tbase; + + size_t expired = kv_store_expire_expire_all(task->kv_store_expire); + size_t size = kv_store_expire_size(task->kv_store_expire); + + plogx_info("%zu/%zu\n", expired, size); +} + +static void stop_last(struct task_base *tbase) +{ + struct task_fm *task = (struct task_fm *)tbase; + + task->dpi_engine.dpi_thread_stop(task->dpi_shared->dpi_opaque); + task->dpi_shared->dpi_opaque = NULL; +} + +static struct task_init task_init_fm = { + .mode_str = "fm", + .init = init_task_fm, + .handle = handle_fm_bulk, + .start = start, + .stop = stop, + .start_first = start_first, + .stop_last = stop_last, + .size = sizeof(struct task_fm) +}; + +__attribute__((constructor)) static void reg_task_fm(void) +{ + reg_task(&task_init_fm); +} diff --git a/VNFs/DPPD-PROX/handle_gen.c b/VNFs/DPPD-PROX/handle_gen.c new file mode 100644 index 00000000..e5e43fca --- /dev/null +++ b/VNFs/DPPD-PROX/handle_gen.c @@ -0,0 +1,1481 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <pcap.h> +#include <string.h> +#include <stdlib.h> +#include <rte_cycles.h> +#include <rte_version.h> +#include <rte_byteorder.h> +#include <rte_ether.h> + +#include "prox_shared.h" +#include "random.h" +#include "prox_malloc.h" +#include "handle_gen.h" +#include "handle_lat.h" +#include "task_init.h" +#include "task_base.h" +#include "prox_port_cfg.h" +#include "lconf.h" +#include "log.h" +#include "quit.h" +#include "prox_cfg.h" +#include "mbuf_utils.h" +#include "qinq.h" +#include "prox_cksum.h" +#include "etypes.h" +#include "prox_assert.h" +#include "prefetch.h" +#include "token_time.h" +#include "local_mbuf.h" +#include "arp.h" +#include "tx_pkt.h" +#include <rte_hash_crc.h> + +struct pkt_template { + uint64_t dst_mac; + uint32_t ip_src; + uint32_t ip_dst_pos; + uint16_t len; + uint16_t l2_len; + uint16_t l3_len; + uint8_t buf[ETHER_MAX_LEN]; +}; + +#define FLAG_DST_MAC_KNOWN 1 +#define FLAG_L3_GEN 2 +#define FLAG_RANDOM_IPS 4 + +#define MAX_TEMPLATE_INDEX 65536 +#define TEMPLATE_INDEX_MASK (MAX_TEMPLATE_INDEX - 1) +#define MBUF_ARP MAX_TEMPLATE_INDEX + +#define IP4(x) x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >> 24 + +static void pkt_template_init_mbuf(struct pkt_template *pkt_template, struct rte_mbuf *mbuf, uint8_t *pkt) +{ + const uint32_t pkt_size = pkt_template->len; + + rte_pktmbuf_pkt_len(mbuf) = pkt_size; + rte_pktmbuf_data_len(mbuf) = pkt_size; + init_mbuf_seg(mbuf); + rte_memcpy(pkt, pkt_template->buf, pkt_template->len); +} + +struct task_gen_pcap { + struct task_base base; + uint64_t hz; + struct local_mbuf local_mbuf; + uint32_t pkt_idx; + struct pkt_template *proto; + uint32_t loop; + uint32_t n_pkts; + uint64_t last_tsc; + uint64_t *proto_tsc; +}; + +struct task_gen { + struct task_base base; + uint64_t hz; + uint64_t link_speed; + struct token_time token_time; + struct local_mbuf local_mbuf; + struct pkt_template *pkt_template; /* packet templates used at runtime */ + uint64_t write_duration_estimate; /* how long it took previously to write the time stamps in the packets */ + uint64_t earliest_tsc_next_pkt; + uint64_t new_rate_bps; + uint64_t pkt_queue_index; + uint32_t n_pkts; /* number of packets in pcap */ + uint32_t pkt_idx; /* current packet from pcap */ + uint32_t pkt_count; /* how many pakets to generate */ + uint32_t runtime_flags; + uint16_t lat_pos; + uint16_t packet_id_pos; + uint16_t accur_pos; + uint16_t sig_pos; + uint32_t sig; + uint8_t generator_id; + uint8_t n_rands; /* number of randoms */ + uint8_t min_bulk_size; + uint8_t max_bulk_size; + uint8_t lat_enabled; + uint8_t runtime_checksum_needed; + struct { + struct random state; + uint32_t rand_mask; /* since the random vals are uniform, masks don't introduce bias */ + uint32_t fixed_bits; /* length of each random (max len = 4) */ + uint16_t rand_offset; /* each random has an offset*/ + uint8_t rand_len; /* # bytes to take from random (no bias introduced) */ + } rand[64]; + uint64_t accur[64]; + uint64_t pkt_tsc_offset[64]; + struct pkt_template *pkt_template_orig; /* packet templates (from inline or from pcap) */ + struct ether_addr gw_mac; + struct ether_addr src_mac; + struct rte_hash *mac_hash; + uint64_t *dst_mac; + uint32_t gw_ip; + uint32_t src_ip; + uint8_t flags; + uint8_t cksum_offload; +} __rte_cache_aligned; + +static inline uint8_t ipv4_get_hdr_len(struct ipv4_hdr *ip) +{ + /* Optimize for common case of IPv4 header without options. */ + if (ip->version_ihl == 0x45) + return sizeof(struct ipv4_hdr); + if (unlikely(ip->version_ihl >> 4 != 4)) { + plog_warn("IPv4 ether_type but IP version = %d != 4", ip->version_ihl >> 4); + return 0; + } + return (ip->version_ihl & 0xF) * 4; +} + +static void parse_l2_l3_len(uint8_t *pkt, uint16_t *l2_len, uint16_t *l3_len, uint16_t len) +{ + *l2_len = sizeof(struct ether_hdr); + *l3_len = 0; + struct vlan_hdr *vlan_hdr; + struct ether_hdr *eth_hdr = (struct ether_hdr*)pkt; + struct ipv4_hdr *ip; + uint16_t ether_type = eth_hdr->ether_type; + + // Unstack VLAN tags + while (((ether_type == ETYPE_8021ad) || (ether_type == ETYPE_VLAN)) && (*l2_len + sizeof(struct vlan_hdr) < len)) { + vlan_hdr = (struct vlan_hdr *)(pkt + *l2_len); + *l2_len +=4; + ether_type = vlan_hdr->eth_proto; + } + + // No L3 cksum offload for IPv6, but TODO L4 offload + // ETYPE_EoGRE CRC not implemented yet + + switch (ether_type) { + case ETYPE_MPLSU: + case ETYPE_MPLSM: + *l2_len +=4; + break; + case ETYPE_IPv4: + break; + case ETYPE_EoGRE: + case ETYPE_ARP: + case ETYPE_IPv6: + *l2_len = 0; + break; + default: + *l2_len = 0; + plog_warn("Unsupported packet type %x - CRC might be wrong\n", ether_type); + break; + } + + if (*l2_len) { + struct ipv4_hdr *ip = (struct ipv4_hdr *)(pkt + *l2_len); + *l3_len = ipv4_get_hdr_len(ip); + } +} + +static void checksum_packet(uint8_t *hdr, struct rte_mbuf *mbuf, struct pkt_template *pkt_template, int cksum_offload) +{ + uint16_t l2_len = pkt_template->l2_len; + uint16_t l3_len = pkt_template->l3_len; + + if (l2_len) { + struct ipv4_hdr *ip = (struct ipv4_hdr*)(hdr + l2_len); + prox_ip_udp_cksum(mbuf, ip, l2_len, l3_len, cksum_offload); + } +} + +static void task_gen_reset_token_time(struct task_gen *task) +{ + token_time_set_bpp(&task->token_time, task->new_rate_bps); + token_time_reset(&task->token_time, rte_rdtsc(), 0); +} + +static void start(struct task_base *tbase) +{ + struct task_gen *task = (struct task_gen *)tbase; + task->pkt_queue_index = 0; + + task_gen_reset_token_time(task); +} + +static void start_pcap(struct task_base *tbase) +{ + struct task_gen_pcap *task = (struct task_gen_pcap *)tbase; + /* When we start, the first packet is sent immediately. */ + task->last_tsc = rte_rdtsc() - task->proto_tsc[0]; + task->pkt_idx = 0; +} + +static void task_gen_take_count(struct task_gen *task, uint32_t send_bulk) +{ + if (task->pkt_count == (uint32_t)-1) + return ; + else { + if (task->pkt_count >= send_bulk) + task->pkt_count -= send_bulk; + else + task->pkt_count = 0; + } +} + +static int handle_gen_pcap_bulk(struct task_base *tbase, struct rte_mbuf **mbuf, uint16_t n_pkts) +{ + struct task_gen_pcap *task = (struct task_gen_pcap *)tbase; + uint64_t now = rte_rdtsc(); + uint64_t send_bulk = 0; + uint32_t pkt_idx_tmp = task->pkt_idx; + + if (pkt_idx_tmp == task->n_pkts) { + PROX_ASSERT(task->loop); + return 0; + } + + for (uint16_t j = 0; j < 64; ++j) { + uint64_t tsc = task->proto_tsc[pkt_idx_tmp]; + if (task->last_tsc + tsc <= now) { + task->last_tsc += tsc; + send_bulk++; + pkt_idx_tmp++; + if (pkt_idx_tmp == task->n_pkts) { + if (task->loop) + pkt_idx_tmp = 0; + else + break; + } + } + else + break; + } + + struct rte_mbuf **new_pkts = local_mbuf_refill_and_take(&task->local_mbuf, send_bulk); + if (new_pkts == NULL) + return 0; + + for (uint16_t j = 0; j < send_bulk; ++j) { + struct rte_mbuf *next_pkt = new_pkts[j]; + struct pkt_template *pkt_template = &task->proto[task->pkt_idx]; + uint8_t *hdr = rte_pktmbuf_mtod(next_pkt, uint8_t *); + + pkt_template_init_mbuf(pkt_template, next_pkt, hdr); + + task->pkt_idx++; + if (task->pkt_idx == task->n_pkts) { + if (task->loop) + task->pkt_idx = 0; + else + break; + } + } + + return task->base.tx_pkt(&task->base, new_pkts, send_bulk, NULL); +} + +static uint64_t bytes_to_tsc(struct task_gen *task, uint32_t bytes) +{ + const uint64_t hz = task->hz; + const uint64_t bytes_per_hz = task->link_speed; + + if (bytes_per_hz == UINT64_MAX) + return 0; + + return hz * bytes / bytes_per_hz; +} + +static uint32_t task_gen_next_pkt_idx(const struct task_gen *task, uint32_t pkt_idx) +{ + return pkt_idx + 1 == task->n_pkts? 0 : pkt_idx + 1; +} + +static uint32_t task_gen_offset_pkt_idx(const struct task_gen *task, uint32_t offset) +{ + return (task->pkt_idx + offset) % task->n_pkts; +} + +static uint32_t task_gen_calc_send_bulk(const struct task_gen *task, uint32_t *total_bytes) +{ + /* The biggest bulk we allow to send is task->max_bulk_size + packets. The max bulk size can also be limited by the + pkt_count field. At the same time, we are rate limiting + based on the specified speed (in bytes per second) so token + bucket based rate limiting must also be applied. The + minimum bulk size is also constrained. If the calculated + bulk size is less then the minimum, then don't send + anything. */ + + const uint32_t min_bulk = task->min_bulk_size; + uint32_t max_bulk = task->max_bulk_size; + + if (task->pkt_count != (uint32_t)-1 && task->pkt_count < max_bulk) { + max_bulk = task->pkt_count; + } + + uint32_t send_bulk = 0; + uint32_t pkt_idx_tmp = task->pkt_idx; + uint32_t would_send_bytes = 0; + uint32_t pkt_size; + + /* + * TODO - this must be improved to take into account the fact that, after applying randoms + * The packet can be replaced by an ARP + */ + for (uint16_t j = 0; j < max_bulk; ++j) { + struct pkt_template *pktpl = &task->pkt_template[pkt_idx_tmp]; + if (unlikely((task->flags & (FLAG_L3_GEN | FLAG_DST_MAC_KNOWN)) == FLAG_L3_GEN)) { + // Generator is supposed to get MAC address - MAC is still unknown for this template + // generate ARP Request to gateway instead of the intended packet + pkt_size = 60; + } else { + pkt_size = pktpl->len; + } + uint32_t pkt_len = pkt_len_to_wire_size(pkt_size); + if (pkt_len + would_send_bytes > task->token_time.bytes_now) + break; + + pkt_idx_tmp = task_gen_next_pkt_idx(task, pkt_idx_tmp); + + send_bulk++; + would_send_bytes += pkt_len; + } + + if (send_bulk < min_bulk) + return 0; + *total_bytes = would_send_bytes; + return send_bulk; +} + +static inline void create_arp(struct rte_mbuf *mbuf, uint8_t *pkt_hdr, uint64_t *src_mac, uint32_t ip_dst, uint32_t ip_src) +{ + uint64_t mac_bcast = 0xFFFFFFFFFFFF; + rte_pktmbuf_pkt_len(mbuf) = 42; + rte_pktmbuf_data_len(mbuf) = 42; + init_mbuf_seg(mbuf); + struct ether_hdr_arp *hdr_arp = (struct ether_hdr_arp *)pkt_hdr; + + memcpy(&hdr_arp->ether_hdr.d_addr.addr_bytes, &mac_bcast, 6); + memcpy(&hdr_arp->ether_hdr.s_addr.addr_bytes, src_mac, 6); + hdr_arp->ether_hdr.ether_type = ETYPE_ARP; + hdr_arp->arp.htype = 0x100, + hdr_arp->arp.ptype = 0x0008; + hdr_arp->arp.hlen = 6; + hdr_arp->arp.plen = 4; + hdr_arp->arp.oper = 0x100; + hdr_arp->arp.data.spa = ip_src; + hdr_arp->arp.data.tpa = ip_dst; + memset(&hdr_arp->arp.data.tha, 0, sizeof(struct ether_addr)); + memcpy(&hdr_arp->arp.data.sha, src_mac, sizeof(struct ether_addr)); +} + +static int task_gen_write_dst_mac(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + uint32_t ip_dst_pos, ip_src_pos, ip_dst, ip_src; + uint16_t i; + int ret; + + if (task->flags & FLAG_L3_GEN) { + if (task->gw_ip) { + if (unlikely((task->flags & FLAG_DST_MAC_KNOWN) == 0)) { + for (i = 0; i < count; ++i) { + struct pkt_template *pktpl = &task->pkt_template[mbufs[i]->udata64 & TEMPLATE_INDEX_MASK]; + create_arp(mbufs[i], pkt_hdr[i], (uint64_t *)&pktpl->buf[6], task->gw_ip, pktpl->ip_src); + mbufs[i]->udata64 |= MBUF_ARP; + } + } else { + for (i = 0; i < count; ++i) { + struct ether_hdr *hdr = (struct ether_hdr *)pkt_hdr[i]; + memcpy(&hdr->d_addr.addr_bytes, &task->gw_mac, 6); + } + } + } else if (unlikely((task->flags & FLAG_RANDOM_IPS) != 0) || (task->n_pkts >= 4)){ + // Find mac in lookup table. Send ARP if not found + int32_t positions[MAX_PKT_BURST], idx; + void *keys[MAX_PKT_BURST]; + uint32_t key[MAX_PKT_BURST]; + for (i = 0; i < count; ++i) { + uint8_t *hdr = (uint8_t *)pkt_hdr[i]; + struct pkt_template *pktpl = &task->pkt_template[mbufs[i]->udata64 & TEMPLATE_INDEX_MASK]; + ip_dst_pos = pktpl->ip_dst_pos; + ip_dst = *(uint32_t *)(hdr + ip_dst_pos); + key[i] = ip_dst; + keys[i] = &key[i]; + } + ret = rte_hash_lookup_bulk(task->mac_hash, (const void **)&keys, count, positions); + if (unlikely(ret < 0)) { + plogx_err("lookup_bulk failed in mac_hash\n"); + tx_pkt_drop_all((struct task_base *)task, mbufs, count, NULL); + return -1; + } + for (i = 0; i < count; ++i) { + idx = positions[i]; + if (unlikely(idx < 0)) { + // mac not found for this IP + struct pkt_template *pktpl = &task->pkt_template[mbufs[i]->udata64 & TEMPLATE_INDEX_MASK]; + uint8_t *hdr = (uint8_t *)pkt_hdr[i]; + ip_src_pos = pktpl->ip_dst_pos - 4; + ip_src = *(uint32_t *)(hdr + ip_src_pos); + create_arp(mbufs[i], pkt_hdr[i], (uint64_t *)&hdr[6], key[i], ip_src); + mbufs[i]->udata64 |= MBUF_ARP; + } else { + // mac found for this IP + struct ether_hdr_arp *hdr_arp = (struct ether_hdr_arp *)pkt_hdr[i]; + memcpy(&hdr_arp->ether_hdr.d_addr.addr_bytes, &task->dst_mac[idx], 6); + } + } + } else { + for (i = 0; i < count; ++i) { + uint8_t *hdr = (uint8_t *)pkt_hdr[i]; + struct pkt_template *pktpl = &task->pkt_template[mbufs[i]->udata64 & TEMPLATE_INDEX_MASK]; + + // Check if packet template already has the mac + if (unlikely(pktpl->dst_mac == 0)) { + // no random_ip, can take from from packet template but no mac (yet) + uint32_t ip_dst_pos = pktpl->ip_dst_pos; + ip_dst = *(uint32_t *)(hdr + ip_dst_pos); + create_arp(mbufs[i], pkt_hdr[i], (uint64_t *)&pktpl->buf[6], ip_dst, pktpl->ip_src); + mbufs[i]->udata64 |= MBUF_ARP; + } else { + // no random ip, mac known + struct ether_hdr_arp *hdr_arp = (struct ether_hdr_arp *)pkt_hdr[i]; + memcpy(&hdr_arp->ether_hdr.d_addr.addr_bytes, &pktpl->dst_mac, 6); + } + } + } + } + return 0; +} + +static void task_gen_apply_random_fields(struct task_gen *task, uint8_t *hdr) +{ + uint32_t ret, ret_tmp; + + for (uint16_t i = 0; i < task->n_rands; ++i) { + ret = random_next(&task->rand[i].state); + ret_tmp = (ret & task->rand[i].rand_mask) | task->rand[i].fixed_bits; + + ret_tmp = rte_bswap32(ret_tmp); + /* At this point, the lower order bytes (BE) contain + the generated value. The address where the values + of interest starts is at ret_tmp + 4 - rand_len. */ + uint8_t *pret_tmp = (uint8_t*)&ret_tmp; + rte_memcpy(hdr + task->rand[i].rand_offset, pret_tmp + 4 - task->rand[i].rand_len, task->rand[i].rand_len); + } +} + +static void task_gen_apply_all_random_fields(struct task_gen *task, uint8_t **pkt_hdr, uint32_t count) +{ + if (!task->n_rands) + return; + + for (uint16_t i = 0; i < count; ++i) + task_gen_apply_random_fields(task, pkt_hdr[i]); +} + +static void task_gen_apply_accur_pos(struct task_gen *task, uint8_t *pkt_hdr, uint32_t accuracy) +{ + *(uint32_t *)(pkt_hdr + task->accur_pos) = accuracy; +} + +static void task_gen_apply_sig(struct task_gen *task, uint8_t *pkt_hdr) +{ + *(uint32_t *)(pkt_hdr + task->sig_pos) = task->sig; +} + +static void task_gen_apply_all_accur_pos(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + if (!task->accur_pos) + return; + + /* The accuracy of task->pkt_queue_index - 64 is stored in + packet task->pkt_queue_index. The ID modulo 64 is the + same. */ + for (uint16_t j = 0; j < count; ++j) { + if ((mbufs[j]->udata64 & MBUF_ARP) == 0) { + uint32_t accuracy = task->accur[(task->pkt_queue_index + j) & 63]; + task_gen_apply_accur_pos(task, pkt_hdr[j], accuracy); + } + } +} + +static void task_gen_apply_all_sig(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + if (!task->sig_pos) + return; + + for (uint16_t j = 0; j < count; ++j) { + if ((mbufs[j]->udata64 & MBUF_ARP) == 0) { + task_gen_apply_sig(task, pkt_hdr[j]); + } + } +} + +static void task_gen_apply_unique_id(struct task_gen *task, uint8_t *pkt_hdr, const struct unique_id *id) +{ + struct unique_id *dst = (struct unique_id *)(pkt_hdr + task->packet_id_pos); + + *dst = *id; +} + +static void task_gen_apply_all_unique_id(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + if (!task->packet_id_pos) + return; + + for (uint16_t i = 0; i < count; ++i) { + if ((mbufs[i]->udata64 & MBUF_ARP) == 0) { + struct unique_id id; + unique_id_init(&id, task->generator_id, task->pkt_queue_index++); + task_gen_apply_unique_id(task, pkt_hdr[i], &id); + } + } +} + +static void task_gen_checksum_packets(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + if (!(task->runtime_flags & TASK_TX_CRC)) + return; + + if (!task->runtime_checksum_needed) + return; + + uint32_t pkt_idx = task_gen_offset_pkt_idx(task, - count); + for (uint16_t i = 0; i < count; ++i) { + if ((mbufs[i]->udata64 & MBUF_ARP) == 0) { + struct pkt_template *pkt_template = &task->pkt_template[pkt_idx]; + checksum_packet(pkt_hdr[i], mbufs[i], pkt_template, task->cksum_offload); + pkt_idx = task_gen_next_pkt_idx(task, pkt_idx); + } + } +} + +static void task_gen_consume_tokens(struct task_gen *task, uint32_t tokens, uint32_t send_count) +{ + /* If max burst has been sent, we can't keep up so just assume + that we can (leaving a "gap" in the packet stream on the + wire) */ + task->token_time.bytes_now -= tokens; + if (send_count == task->max_bulk_size && task->token_time.bytes_now > tokens) { + task->token_time.bytes_now = tokens; + } +} + +static uint64_t task_gen_calc_bulk_duration(struct task_gen *task, uint32_t count) +{ + uint32_t pkt_idx = task_gen_offset_pkt_idx(task, - 1); + struct pkt_template *last_pkt_template = &task->pkt_template[pkt_idx]; + uint32_t last_pkt_len = pkt_len_to_wire_size(last_pkt_template->len); + uint64_t last_pkt_duration = bytes_to_tsc(task, last_pkt_len); + uint64_t bulk_duration = task->pkt_tsc_offset[count - 1] + last_pkt_duration; + + return bulk_duration; +} + +static uint64_t task_gen_write_latency(struct task_gen *task, uint8_t **pkt_hdr, uint32_t count) +{ + if (!task->lat_enabled) + return 0; + + uint64_t tx_tsc, delta_t; + uint64_t tsc_before_tx = 0; + + /* Just before sending the packets, apply the time stamp + relative to when the first packet will be sent. The first + packet will be sent now. The time is read for each packet + to reduce the error towards the actual time the packet will + be sent. */ + uint64_t write_tsc_after, write_tsc_before; + + write_tsc_before = rte_rdtsc(); + + /* The time it took previously to write the time stamps in the + packets is used as an estimate for how long it will take to + write the time stamps now. The estimated time at which the + packets will actually be sent will be at tx_tsc. */ + tx_tsc = write_tsc_before + task->write_duration_estimate; + + /* The offset delta_t tracks the difference between the actual + time and the time written in the packets. Adding the offset + to the actual time insures that the time written in the + packets is monotonically increasing. At the same time, + simply sleeping until delta_t is zero would leave a period + of silence on the line. The error has been introduced + earlier, but the packets have already been sent. */ + if (tx_tsc < task->earliest_tsc_next_pkt) + delta_t = task->earliest_tsc_next_pkt - tx_tsc; + else + delta_t = 0; + + for (uint16_t i = 0; i < count; ++i) { + uint32_t *pos = (uint32_t *)(pkt_hdr[i] + task->lat_pos); + const uint64_t pkt_tsc = tx_tsc + delta_t + task->pkt_tsc_offset[i]; + + *pos = pkt_tsc >> LATENCY_ACCURACY; + } + + uint64_t bulk_duration = task_gen_calc_bulk_duration(task, count); + + task->earliest_tsc_next_pkt = tx_tsc + delta_t + bulk_duration; + write_tsc_after = rte_rdtsc(); + task->write_duration_estimate = write_tsc_after - write_tsc_before; + + /* Make sure that the time stamps that were written + are valid. The offset must be taken into account */ + do { + tsc_before_tx = rte_rdtsc(); + } while (tsc_before_tx < tx_tsc); + return tsc_before_tx; +} + +static void task_gen_store_accuracy(struct task_gen *task, uint32_t count, uint64_t tsc_before_tx) +{ + if (!task->accur_pos) + return; + + uint64_t accur = rte_rdtsc() - tsc_before_tx; + uint64_t first_accuracy_idx = task->pkt_queue_index - count; + + for (uint32_t i = 0; i < count; ++i) { + uint32_t accuracy_idx = (first_accuracy_idx + i) & 63; + + task->accur[accuracy_idx] = accur; + } +} + +static void task_gen_load_and_prefetch(struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + for (uint16_t i = 0; i < count; ++i) + rte_prefetch0(mbufs[i]); + for (uint16_t i = 0; i < count; ++i) + pkt_hdr[i] = rte_pktmbuf_mtod(mbufs[i], uint8_t *); + for (uint16_t i = 0; i < count; ++i) + rte_prefetch0(pkt_hdr[i]); +} + +static void task_gen_build_packets(struct task_gen *task, struct rte_mbuf **mbufs, uint8_t **pkt_hdr, uint32_t count) +{ + uint64_t will_send_bytes = 0; + + for (uint16_t i = 0; i < count; ++i) { + struct pkt_template *pktpl = &task->pkt_template[task->pkt_idx]; + struct pkt_template *pkt_template = &task->pkt_template[task->pkt_idx]; + pkt_template_init_mbuf(pkt_template, mbufs[i], pkt_hdr[i]); + mbufs[i]->udata64 = task->pkt_idx & TEMPLATE_INDEX_MASK; + struct ether_hdr *hdr = (struct ether_hdr *)pkt_hdr[i]; + if (task->lat_enabled) { + task->pkt_tsc_offset[i] = bytes_to_tsc(task, will_send_bytes); + will_send_bytes += pkt_len_to_wire_size(pkt_template->len); + } + task->pkt_idx = task_gen_next_pkt_idx(task, task->pkt_idx); + } +} + +static void task_gen_update_config(struct task_gen *task) +{ + if (task->token_time.cfg.bpp != task->new_rate_bps) + task_gen_reset_token_time(task); +} + +static inline void handle_arp_pkts(struct task_gen *task, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + int j; + int ret; + struct ether_hdr_arp *hdr; + uint8_t out[MAX_PKT_BURST]; + static struct my_arp_t arp_reply = { + .htype = 0x100, + .ptype = 8, + .hlen = 6, + .plen = 4, + .oper = 0x200 + }; + static struct my_arp_t arp_request = { + .htype = 0x100, + .ptype = 8, + .hlen = 6, + .plen = 4, + .oper = 0x100 + }; + + for (j = 0; j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 0; j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void *)); + } + for (j = 0; j < n_pkts; ++j) { + hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr_arp *); + if (hdr->ether_hdr.ether_type == ETYPE_ARP) { + if (memcmp(&hdr->arp, &arp_reply, 8) == 0) { + uint32_t ip = hdr->arp.data.spa; + // plog_info("Received ARP Reply for IP %x\n",ip); + if (ip == task->gw_ip) { + memcpy(&task->gw_mac, &hdr->arp.data.sha, 6);; + task->flags |= FLAG_DST_MAC_KNOWN; + out[j] = OUT_HANDLED; + continue; + } else if ((task->n_pkts >= 4) || (task->flags & FLAG_RANDOM_IPS)) { + // Ideally, we should add the key when making the arp request, + // We should only store the mac address key was created. + // Here we are storing MAC we did not asked for... + ret = rte_hash_add_key(task->mac_hash, (const void *)&ip); + if (ret < 0) { + plogx_info("Unable add ip %d.%d.%d.%d in mac_hash\n", IP4(ip)); + out[j] = OUT_DISCARD; + } else { + task->dst_mac[ret] = *(uint64_t *)&(hdr->arp.data.sha); + out[j] = OUT_HANDLED; + } + continue; + } + // Need to find template back... + // Only try this if there are few templates + for (unsigned int idx = 0; idx < task->n_pkts; idx++) { + struct pkt_template *pktpl = &task->pkt_template[idx]; + uint32_t ip_dst_pos = pktpl->ip_dst_pos; + uint32_t *ip_dst = (uint32_t *)(((uint8_t *)pktpl->buf) + ip_dst_pos); + if (*ip_dst == ip) { + pktpl->dst_mac = *(uint64_t *)&(hdr->arp.data.sha); + } + out[j] = OUT_HANDLED; + } + } else if (memcmp(&hdr->arp, &arp_request, 8) == 0) { + struct ether_addr s_addr; + if (!task->src_ip) { + create_mac(hdr, &s_addr); + prepare_arp_reply(hdr, &s_addr); + memcpy(hdr->ether_hdr.d_addr.addr_bytes, hdr->ether_hdr.s_addr.addr_bytes, 6); + memcpy(hdr->ether_hdr.s_addr.addr_bytes, &s_addr, 6); + out[j] = 0; + } else if (hdr->arp.data.tpa == task->src_ip) { + prepare_arp_reply(hdr, &task->src_mac); + memcpy(hdr->ether_hdr.d_addr.addr_bytes, hdr->ether_hdr.s_addr.addr_bytes, 6); + memcpy(hdr->ether_hdr.s_addr.addr_bytes, &task->src_mac, 6); + out[j] = 0; + } else { + out[j] = OUT_DISCARD; + plogx_dbg("Received ARP on unexpected IP %x, expecting %x\n", rte_be_to_cpu_32(hdr->arp.data.tpa), rte_be_to_cpu_32(task->src_ip)); + } + } + } else { + out[j] = OUT_DISCARD; + } + } + ret = task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int handle_gen_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_gen *task = (struct task_gen *)tbase; + uint8_t out[MAX_PKT_BURST] = {0}; + int ret; + + int i, j; + + if (unlikely((task->flags & FLAG_L3_GEN) && (n_pkts != 0))) { + handle_arp_pkts(task, mbufs, n_pkts); + } + + task_gen_update_config(task); + + if (task->pkt_count == 0) { + task_gen_reset_token_time(task); + return 0; + } + if (!task->token_time.cfg.bpp) + return 0; + + token_time_update(&task->token_time, rte_rdtsc()); + + uint32_t would_send_bytes; + const uint32_t send_bulk = task_gen_calc_send_bulk(task, &would_send_bytes); + + if (send_bulk == 0) + return 0; + task_gen_take_count(task, send_bulk); + task_gen_consume_tokens(task, would_send_bytes, send_bulk); + + struct rte_mbuf **new_pkts = local_mbuf_refill_and_take(&task->local_mbuf, send_bulk); + if (new_pkts == NULL) + return 0; + uint8_t *pkt_hdr[MAX_RING_BURST]; + + task_gen_load_and_prefetch(new_pkts, pkt_hdr, send_bulk); + task_gen_build_packets(task, new_pkts, pkt_hdr, send_bulk); + task_gen_apply_all_random_fields(task, pkt_hdr, send_bulk); + if (task_gen_write_dst_mac(task, new_pkts, pkt_hdr, send_bulk) < 0) + return 0; + task_gen_apply_all_accur_pos(task, new_pkts, pkt_hdr, send_bulk); + task_gen_apply_all_sig(task, new_pkts, pkt_hdr, send_bulk); + task_gen_apply_all_unique_id(task, new_pkts, pkt_hdr, send_bulk); + + uint64_t tsc_before_tx; + + tsc_before_tx = task_gen_write_latency(task, pkt_hdr, send_bulk); + task_gen_checksum_packets(task, new_pkts, pkt_hdr, send_bulk); + ret = task->base.tx_pkt(&task->base, new_pkts, send_bulk, out); + task_gen_store_accuracy(task, send_bulk, tsc_before_tx); + return ret; +} + +static void init_task_gen_seeds(struct task_gen *task) +{ + for (size_t i = 0; i < sizeof(task->rand)/sizeof(task->rand[0]); ++i) + random_init_seed(&task->rand[i].state); +} + +static uint32_t pcap_count_pkts(pcap_t *handle) +{ + struct pcap_pkthdr header; + const uint8_t *buf; + uint32_t ret = 0; + long pkt1_fpos = ftell(pcap_file(handle)); + + while ((buf = pcap_next(handle, &header))) { + ret++; + } + int ret2 = fseek(pcap_file(handle), pkt1_fpos, SEEK_SET); + PROX_PANIC(ret2 != 0, "Failed to reset reading pcap file\n"); + return ret; +} + +static uint64_t avg_time_stamp(uint64_t *time_stamp, uint32_t n) +{ + uint64_t tot_inter_pkt = 0; + + for (uint32_t i = 0; i < n; ++i) + tot_inter_pkt += time_stamp[i]; + return (tot_inter_pkt + n / 2)/n; +} + +static int pcap_read_pkts(pcap_t *handle, const char *file_name, uint32_t n_pkts, struct pkt_template *proto, uint64_t *time_stamp) +{ + struct pcap_pkthdr header; + const uint8_t *buf; + size_t len; + + for (uint32_t i = 0; i < n_pkts; ++i) { + buf = pcap_next(handle, &header); + + PROX_PANIC(buf == NULL, "Failed to read packet %d from pcap %s\n", i, file_name); + proto[i].len = header.len; + len = RTE_MIN(header.len, sizeof(proto[i].buf)); + if (header.len > len) + plogx_warn("Packet truncated from %u to %zu bytes\n", header.len, len); + + if (time_stamp) { + static struct timeval beg; + struct timeval tv; + + if (i == 0) + beg = header.ts; + + tv = tv_diff(&beg, &header.ts); + tv_to_tsc(&tv, time_stamp + i); + } + rte_memcpy(proto[i].buf, buf, len); + } + + if (time_stamp && n_pkts) { + for (uint32_t i = n_pkts - 1; i > 0; --i) + time_stamp[i] -= time_stamp[i - 1]; + /* Since the handle function will loop the packets, + there is one time-stamp that is not provided by the + pcap file. This is the time between the last and + the first packet. This implementation takes the + average of the inter-packet times here. */ + if (n_pkts > 1) + time_stamp[0] = avg_time_stamp(time_stamp + 1, n_pkts - 1); + } + + return 0; +} + +static int check_pkt_size(struct task_gen *task, uint32_t pkt_size, int do_panic) +{ + const uint16_t min_len = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr); + const uint16_t max_len = ETHER_MAX_LEN - 4; + + if (do_panic) { + PROX_PANIC(pkt_size == 0, "Invalid packet size length (no packet defined?)\n"); + PROX_PANIC(pkt_size > max_len, "pkt_size out of range (must be <= %u)\n", max_len); + PROX_PANIC(pkt_size < min_len, "pkt_size out of range (must be >= %u)\n", min_len); + return 0; + } else { + if (pkt_size == 0) { + plog_err("Invalid packet size length (no packet defined?)\n"); + return -1; + } + if (pkt_size > max_len) { + plog_err("pkt_size out of range (must be <= %u)\n", max_len); + return -1; + } + if (pkt_size < min_len) { + plog_err("pkt_size out of range (must be >= %u)\n", min_len); + return -1; + } + return 0; + } +} + +static int check_all_pkt_size(struct task_gen *task, int do_panic) +{ + int rc; + for (uint32_t i = 0; i < task->n_pkts;++i) { + if ((rc = check_pkt_size(task, task->pkt_template[i].len, do_panic)) != 0) + return rc; + } + return 0; +} + +static void check_fields_in_bounds(struct task_gen *task) +{ + const uint32_t pkt_size = task->pkt_template[0].len; + + if (task->lat_enabled) { + uint32_t pos_beg = task->lat_pos; + uint32_t pos_end = task->lat_pos + 3U; + + PROX_PANIC(pkt_size <= pos_end, "Writing latency at %u-%u, but packet size is %u bytes\n", + pos_beg, pos_end, pkt_size); + } + if (task->packet_id_pos) { + uint32_t pos_beg = task->packet_id_pos; + uint32_t pos_end = task->packet_id_pos + 4U; + + PROX_PANIC(pkt_size <= pos_end, "Writing packet at %u-%u, but packet size is %u bytes\n", + pos_beg, pos_end, pkt_size); + } + if (task->accur_pos) { + uint32_t pos_beg = task->accur_pos; + uint32_t pos_end = task->accur_pos + 3U; + + PROX_PANIC(pkt_size <= pos_end, "Writing accuracy at %u%-u, but packet size is %u bytes\n", + pos_beg, pos_end, pkt_size); + } +} + +static void task_gen_pkt_template_recalc_metadata(struct task_gen *task) +{ + struct pkt_template *template; + + for (size_t i = 0; i < task->n_pkts; ++i) { + template = &task->pkt_template[i]; + parse_l2_l3_len(template->buf, &template->l2_len, &template->l3_len, template->len); + } +} + +static void task_gen_pkt_template_recalc_checksum(struct task_gen *task) +{ + struct pkt_template *template; + struct ipv4_hdr *ip; + + task->runtime_checksum_needed = 0; + for (size_t i = 0; i < task->n_pkts; ++i) { + template = &task->pkt_template[i]; + if (template->l2_len == 0) + continue; + ip = (struct ipv4_hdr *)(template->buf + template->l2_len); + + ip->hdr_checksum = 0; + prox_ip_cksum_sw(ip); + uint32_t l4_len = rte_bswap16(ip->total_length) - template->l3_len; + + if (ip->next_proto_id == IPPROTO_UDP) { + struct udp_hdr *udp = (struct udp_hdr *)(((uint8_t *)ip) + template->l3_len); + prox_udp_cksum_sw(udp, l4_len, ip->src_addr, ip->dst_addr); + } else if (ip->next_proto_id == IPPROTO_TCP) { + struct tcp_hdr *tcp = (struct tcp_hdr *)(((uint8_t *)ip) + template->l3_len); + prox_tcp_cksum_sw(tcp, l4_len, ip->src_addr, ip->dst_addr); + } + + /* The current implementation avoids checksum + calculation by determining that at packet + construction time, no fields are applied that would + require a recalculation of the checksum. */ + if (task->lat_enabled && task->lat_pos > template->l2_len) + task->runtime_checksum_needed = 1; + if (task->accur_pos > template->l2_len) + task->runtime_checksum_needed = 1; + if (task->packet_id_pos > template->l2_len) + task->runtime_checksum_needed = 1; + } +} + +static void task_gen_pkt_template_recalc_all(struct task_gen *task) +{ + task_gen_pkt_template_recalc_metadata(task); + task_gen_pkt_template_recalc_checksum(task); +} + +static void task_gen_reset_pkt_templates_len(struct task_gen *task) +{ + struct pkt_template *src, *dst; + + for (size_t i = 0; i < task->n_pkts; ++i) { + src = &task->pkt_template_orig[i]; + dst = &task->pkt_template[i]; + dst->len = src->len; + } +} + +static void task_gen_reset_pkt_templates_content(struct task_gen *task) +{ + struct pkt_template *src, *dst; + + for (size_t i = 0; i < task->n_pkts; ++i) { + src = &task->pkt_template_orig[i]; + dst = &task->pkt_template[i]; + memcpy(dst->buf, src->buf, dst->len); + } +} + +static void task_gen_reset_pkt_templates(struct task_gen *task) +{ + task_gen_reset_pkt_templates_len(task); + task_gen_reset_pkt_templates_content(task); + task_gen_pkt_template_recalc_all(task); +} + +static void task_init_gen_load_pkt_inline(struct task_gen *task, struct task_args *targ) +{ + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + if (targ->pkt_size > sizeof(task->pkt_template[0].buf)) + targ->pkt_size = sizeof(task->pkt_template[0].buf); + task->n_pkts = 1; + + size_t mem_size = task->n_pkts * sizeof(*task->pkt_template); + task->pkt_template = prox_zmalloc(mem_size, socket_id); + task->pkt_template_orig = prox_zmalloc(mem_size, socket_id); + + PROX_PANIC(task->pkt_template == NULL || + task->pkt_template_orig == NULL, + "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size); + + rte_memcpy(task->pkt_template_orig[0].buf, targ->pkt_inline, targ->pkt_size); + task->pkt_template_orig[0].len = targ->pkt_size; + task_gen_reset_pkt_templates(task); + check_all_pkt_size(task, 1); + check_fields_in_bounds(task); +} + +static void task_init_gen_load_pcap(struct task_gen *task, struct task_args *targ) +{ + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + char err[PCAP_ERRBUF_SIZE]; + pcap_t *handle = pcap_open_offline(targ->pcap_file, err); + PROX_PANIC(handle == NULL, "Failed to open PCAP file: %s\n", err); + + task->n_pkts = pcap_count_pkts(handle); + plogx_info("%u packets in pcap file '%s'\n", task->n_pkts, targ->pcap_file); + + if (targ->n_pkts) + task->n_pkts = RTE_MIN(task->n_pkts, targ->n_pkts); + PROX_PANIC(task->n_pkts > MAX_TEMPLATE_INDEX, "Too many packets specified in pcap - increase MAX_TEMPLATE_INDEX\n"); + plogx_info("Loading %u packets from pcap\n", task->n_pkts); + size_t mem_size = task->n_pkts * sizeof(*task->pkt_template); + task->pkt_template = prox_zmalloc(mem_size, socket_id); + task->pkt_template_orig = prox_zmalloc(mem_size, socket_id); + PROX_PANIC(task->pkt_template == NULL || + task->pkt_template_orig == NULL, + "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size); + + pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->pkt_template_orig, NULL); + pcap_close(handle); + task_gen_reset_pkt_templates(task); +} + +static struct rte_mempool *task_gen_create_mempool(struct task_args *targ) +{ + static char name[] = "gen_pool"; + struct rte_mempool *ret; + const int sock_id = rte_lcore_to_socket_id(targ->lconf->id); + + name[0]++; + ret = rte_mempool_create(name, targ->nb_mbuf - 1, MBUF_SIZE, + targ->nb_cache_mbuf, sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, 0, + sock_id, 0); + PROX_PANIC(ret == NULL, "Failed to allocate dummy memory pool on socket %u with %u elements\n", + sock_id, targ->nb_mbuf - 1); + return ret; +} + +void task_gen_set_pkt_count(struct task_base *tbase, uint32_t count) +{ + struct task_gen *task = (struct task_gen *)tbase; + + task->pkt_count = count; +} + +int task_gen_set_pkt_size(struct task_base *tbase, uint32_t pkt_size) +{ + struct task_gen *task = (struct task_gen *)tbase; + int rc; + + task->pkt_template[0].len = pkt_size; + if ((rc = check_all_pkt_size(task, 0)) != 0) + return rc; + check_fields_in_bounds(task); + return rc; +} + +void task_gen_set_gateway_ip(struct task_base *tbase, uint32_t ip) +{ + struct task_gen *task = (struct task_gen *)tbase; + task->gw_ip = ip; + task->flags &= ~FLAG_DST_MAC_KNOWN; +} + +void task_gen_set_rate(struct task_base *tbase, uint64_t bps) +{ + struct task_gen *task = (struct task_gen *)tbase; + + task->new_rate_bps = bps; +} + +void task_gen_reset_randoms(struct task_base *tbase) +{ + struct task_gen *task = (struct task_gen *)tbase; + + for (uint32_t i = 0; i < task->n_rands; ++i) { + task->rand[i].rand_mask = 0; + task->rand[i].fixed_bits = 0; + task->rand[i].rand_offset = 0; + } + task->n_rands = 0; + task->flags &= ~FLAG_RANDOM_IPS; +} + +int task_gen_set_value(struct task_base *tbase, uint32_t value, uint32_t offset, uint32_t len) +{ + struct task_gen *task = (struct task_gen *)tbase; + + for (size_t i = 0; i < task->n_pkts; ++i) { + uint32_t to_write = rte_cpu_to_be_32(value) >> ((4 - len) * 8); + uint8_t *dst = task->pkt_template[i].buf; + + rte_memcpy(dst + offset, &to_write, len); + } + + task_gen_pkt_template_recalc_all(task); + + return 0; +} + +void task_gen_reset_values(struct task_base *tbase) +{ + struct task_gen *task = (struct task_gen *)tbase; + + task_gen_reset_pkt_templates_content(task); +} + +uint32_t task_gen_get_n_randoms(struct task_base *tbase) +{ + struct task_gen *task = (struct task_gen *)tbase; + + return task->n_rands; +} + +static void init_task_gen_pcap(struct task_base *tbase, struct task_args *targ) +{ + struct task_gen_pcap *task = (struct task_gen_pcap *)tbase; + const uint32_t sockid = rte_lcore_to_socket_id(targ->lconf->id); + + task->loop = targ->loop; + task->pkt_idx = 0; + task->hz = rte_get_tsc_hz(); + + task->local_mbuf.mempool = task_gen_create_mempool(targ); + + PROX_PANIC(!strcmp(targ->pcap_file, ""), "No pcap file defined\n"); + + char err[PCAP_ERRBUF_SIZE]; + pcap_t *handle = pcap_open_offline(targ->pcap_file, err); + PROX_PANIC(handle == NULL, "Failed to open PCAP file: %s\n", err); + + task->n_pkts = pcap_count_pkts(handle); + plogx_info("%u packets in pcap file '%s'\n", task->n_pkts, targ->pcap_file); + + if (targ->n_pkts) { + plogx_info("Configured to load %u packets\n", targ->n_pkts); + if (task->n_pkts > targ->n_pkts) + task->n_pkts = targ->n_pkts; + } + PROX_PANIC(task->n_pkts > MAX_TEMPLATE_INDEX, "Too many packets specified in pcap - increase MAX_TEMPLATE_INDEX\n"); + + plogx_info("Loading %u packets from pcap\n", task->n_pkts); + + size_t mem_size = task->n_pkts * (sizeof(*task->proto) + sizeof(*task->proto_tsc)); + uint8_t *mem = prox_zmalloc(mem_size, sockid); + + PROX_PANIC(mem == NULL, "Failed to allocate %lu bytes (in huge pages) for pcap file\n", mem_size); + task->proto = (struct pkt_template *) mem; + task->proto_tsc = (uint64_t *)(mem + task->n_pkts * sizeof(*task->proto)); + + pcap_read_pkts(handle, targ->pcap_file, task->n_pkts, task->proto, task->proto_tsc); + pcap_close(handle); +} + +static int task_gen_find_random_with_offset(struct task_gen *task, uint32_t offset) +{ + for (uint32_t i = 0; i < task->n_rands; ++i) { + if (task->rand[i].rand_offset == offset) { + return i; + } + } + + return UINT32_MAX; +} + +int task_gen_add_rand(struct task_base *tbase, const char *rand_str, uint32_t offset, uint32_t rand_id) +{ + struct task_gen *task = (struct task_gen *)tbase; + uint32_t existing_rand; + + if (rand_id == UINT32_MAX && task->n_rands == 64) { + plog_err("Too many randoms\n"); + return -1; + } + uint32_t mask, fixed, len; + + if (parse_random_str(&mask, &fixed, &len, rand_str)) { + plog_err("%s\n", get_parse_err()); + return -1; + } + task->runtime_checksum_needed = 1; + + existing_rand = task_gen_find_random_with_offset(task, offset); + if (existing_rand != UINT32_MAX) { + plog_warn("Random at offset %d already set => overwriting len = %d %s\n", offset, len, rand_str); + rand_id = existing_rand; + task->rand[rand_id].rand_len = len; + task->rand[rand_id].rand_offset = offset; + task->rand[rand_id].rand_mask = mask; + task->rand[rand_id].fixed_bits = fixed; + return 0; + } + + task->rand[task->n_rands].rand_len = len; + task->rand[task->n_rands].rand_offset = offset; + task->rand[task->n_rands].rand_mask = mask; + task->rand[task->n_rands].fixed_bits = fixed; + + struct pkt_template *pktpl = &task->pkt_template[0]; + if (!((offset >= pktpl->ip_dst_pos + 4) || (offset + len < pktpl->ip_dst_pos))) { + plog_info("\tUsing randoms IP destinations\n"); + task->flags |= FLAG_RANDOM_IPS; + } + + task->n_rands++; + return 0; +} + +static void init_task_gen_early(struct task_args *targ) +{ + uint8_t *generator_count = prox_sh_find_system("generator_count"); + + if (generator_count == NULL) { + generator_count = prox_zmalloc(sizeof(*generator_count), 0); + prox_sh_add_system("generator_count", generator_count); + } + targ->generator_id = *generator_count; + (*generator_count)++; +} + +static void init_task_gen(struct task_base *tbase, struct task_args *targ) +{ + struct task_gen *task = (struct task_gen *)tbase; + + task->packet_id_pos = targ->packet_id_pos; + + task->local_mbuf.mempool = task_gen_create_mempool(targ); + PROX_PANIC(task->local_mbuf.mempool == NULL, "Failed to create mempool\n"); + task->pkt_idx = 0; + task->hz = rte_get_tsc_hz(); + task->lat_pos = targ->lat_pos; + task->accur_pos = targ->accur_pos; + task->sig_pos = targ->sig_pos; + task->sig = targ->sig; + task->new_rate_bps = targ->rate_bps; + + struct token_time_cfg tt_cfg = token_time_cfg_create(1250000000, rte_get_tsc_hz(), -1); + + token_time_init(&task->token_time, &tt_cfg); + init_task_gen_seeds(task); + + task->min_bulk_size = targ->min_bulk_size; + task->max_bulk_size = targ->max_bulk_size; + if (task->min_bulk_size < 1) + task->min_bulk_size = 1; + if (task->max_bulk_size < 1) + task->max_bulk_size = 64; + PROX_PANIC(task->max_bulk_size > 64, "max_bulk_size higher than 64\n"); + PROX_PANIC(task->max_bulk_size < task->min_bulk_size, "max_bulk_size must be > than min_bulk_size\n"); + + task->pkt_count = -1; + task->lat_enabled = targ->lat_enabled; + task->runtime_flags = targ->runtime_flags; + PROX_PANIC((task->lat_pos || task->accur_pos) && !task->lat_enabled, "lat not enabled by lat pos or accur pos configured\n"); + + task->generator_id = targ->generator_id; + task->link_speed = UINT64_MAX; + if (targ->nb_txrings == 0 && targ->nb_txports == 1) + task->link_speed = 1250000000; + + if (!strcmp(targ->pcap_file, "")) { + plog_info("\tUsing inline definition of a packet\n"); + task_init_gen_load_pkt_inline(task, targ); + } else { + plog_info("Loading from pcap %s\n", targ->pcap_file); + task_init_gen_load_pcap(task, targ); + } + + if ((targ->flags & DSF_KEEP_SRC_MAC) == 0 && (targ->nb_txrings || targ->nb_txports)) { + uint8_t *src_addr = prox_port_cfg[tbase->tx_params_hw.tx_port_queue->port].eth_addr.addr_bytes; + for (uint32_t i = 0; i < task->n_pkts; ++i) { + rte_memcpy(&task->pkt_template[i].buf[6], src_addr, 6); + } + } + memcpy(&task->src_mac, &prox_port_cfg[task->base.tx_params_hw.tx_port_queue->port].eth_addr, sizeof(struct ether_addr)); + if (!strcmp(targ->task_init->sub_mode_str, "l3")) { + // In L3 GEN, we need to receive ARP replies + task->flags = FLAG_L3_GEN; + task->gw_ip = rte_cpu_to_be_32(targ->gateway_ipv4); + uint32_t n_entries; + + if (targ->number_gen_ip == 0) + n_entries = 1048576; + else + n_entries = targ->number_gen_ip; + + static char hash_name[30]; + sprintf(hash_name, "A%03d_mac_table", targ->lconf->id); + + struct rte_hash_parameters hash_params = { + .name = hash_name, + .entries = n_entries, + .key_len = sizeof(uint32_t), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + }; + task->mac_hash = rte_hash_create(&hash_params); + PROX_PANIC(task->mac_hash == NULL, "Failed to set up mac hash table for %d IP\n", n_entries); + + const uint32_t socket = rte_lcore_to_socket_id(targ->lconf->id); + task->dst_mac = (uint64_t *)prox_zmalloc(n_entries * sizeof(uint64_t), socket); + PROX_PANIC(task->dst_mac == NULL, "Failed to allocate mac table for %d IP\n", n_entries); + + for (uint32_t i = 0; i < task->n_pkts; ++i) { + // For all destination IP, ARP request will need to be sent + // Store position of Destination IP in template + int ip_dst_pos = 0; + int maybe_ipv4 = 0; + int l2_len = sizeof(struct ether_hdr); + struct vlan_hdr *vlan_hdr; + uint8_t *pkt = task->pkt_template[i].buf; + struct ether_hdr *eth_hdr = (struct ether_hdr*)pkt; + struct ipv4_hdr *ip; + uint16_t ether_type = eth_hdr->ether_type; + + // Unstack VLAN tags + while (((ether_type == ETYPE_8021ad) || (ether_type == ETYPE_VLAN)) && (l2_len + sizeof(struct vlan_hdr) < task->pkt_template[i].len)) { + vlan_hdr = (struct vlan_hdr *)(pkt + l2_len); + l2_len +=4; + ether_type = vlan_hdr->eth_proto; + } + if ((ether_type == ETYPE_MPLSU) || (ether_type == ETYPE_MPLSM)) { + l2_len +=4; + maybe_ipv4 = 1; + } + if ((ether_type == ETYPE_IPv4) || maybe_ipv4) { + struct ipv4_hdr *ip = (struct ipv4_hdr *)(pkt + l2_len); + PROX_PANIC(ip->version_ihl >> 4 != 4, "IPv4 ether_type but IP version = %d != 4", ip->version_ihl >> 4); + // Even if IPv4 header contains options, options are after ip src and dst + ip_dst_pos = l2_len + sizeof(struct ipv4_hdr) - sizeof(uint32_t); + uint32_t *p = ((uint32_t *)(task->pkt_template[i].buf + ip_dst_pos - sizeof(uint32_t))); + task->pkt_template[i].ip_dst_pos = ip_dst_pos; + task->pkt_template[i].ip_src = *p; + uint32_t *p1 = ((uint32_t *)(task->pkt_template[i].buf + ip_dst_pos)); + plog_info("\tip_dst_pos = %d, ip_dst = %x\n", ip_dst_pos, *p1); + } + } + task->src_ip = rte_cpu_to_be_32(targ->local_ipv4); + } + for (uint32_t i = 0; i < targ->n_rand_str; ++i) { + PROX_PANIC(task_gen_add_rand(tbase, targ->rand_str[i], targ->rand_offset[i], UINT32_MAX), + "Failed to add random\n"); + } + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->cksum_offload = port->capabilities.tx_offload_cksum; + } +} + +static struct task_init task_init_gen = { + .mode_str = "gen", + .init = init_task_gen, + .handle = handle_gen_bulk, + .start = start, +#ifdef SOFT_CRC + // For SOFT_CRC, no offload is needed. If both NOOFFLOADS and NOMULTSEGS flags are set the + // vector mode is used by DPDK, resulting (theoretically) in higher performance. + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, +#else + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX, +#endif + .size = sizeof(struct task_gen) +}; + +static struct task_init task_init_gen_l3 = { + .mode_str = "gen", + .sub_mode_str = "l3", + .init = init_task_gen, + .handle = handle_gen_bulk, + .start = start, +#ifdef SOFT_CRC + // For SOFT_CRC, no offload is needed. If both NOOFFLOADS and NOMULTSEGS flags are set the + // vector mode is used by DPDK, resulting (theoretically) in higher performance. + .flag_features = TASK_FEATURE_ZERO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_ZERO_RX, +#else + .flag_features = TASK_FEATURE_ZERO_RX, +#endif + .size = sizeof(struct task_gen) +}; + +static struct task_init task_init_gen_pcap = { + .mode_str = "gen", + .sub_mode_str = "pcap", + .init = init_task_gen_pcap, + .handle = handle_gen_pcap_bulk, + .start = start_pcap, +#ifdef SOFT_CRC + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, +#else + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_NO_RX, +#endif + .size = sizeof(struct task_gen_pcap) +}; + +__attribute__((constructor)) static void reg_task_gen(void) +{ + reg_task(&task_init_gen); + reg_task(&task_init_gen_l3); + reg_task(&task_init_gen_pcap); +} diff --git a/VNFs/DPPD-PROX/handle_gen.h b/VNFs/DPPD-PROX/handle_gen.h new file mode 100644 index 00000000..6f00ca12 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_gen.h @@ -0,0 +1,51 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_GEN_H_ +#define _HANDLE_GEN_H_ + +struct unique_id { + uint8_t generator_id; + uint32_t packet_id; +} __attribute__((packed)); + +static void unique_id_init(struct unique_id *unique_id, uint8_t generator_id, uint32_t packet_id) +{ + unique_id->generator_id = generator_id; + unique_id->packet_id = packet_id; +} + +static void unique_id_get(struct unique_id *unique_id, uint8_t *generator_id, uint32_t *packet_id) +{ + *generator_id = unique_id->generator_id; + *packet_id = unique_id->packet_id; +} + +struct task_base; + +void task_gen_set_pkt_count(struct task_base *tbase, uint32_t count); +int task_gen_set_pkt_size(struct task_base *tbase, uint32_t pkt_size); +void task_gen_set_rate(struct task_base *tbase, uint64_t bps); +void task_gen_set_gateway_ip(struct task_base *tbase, uint32_t ip); +void task_gen_reset_randoms(struct task_base *tbase); +void task_gen_reset_values(struct task_base *tbase); +int task_gen_set_value(struct task_base *tbase, uint32_t value, uint32_t offset, uint32_t len); +int task_gen_add_rand(struct task_base *tbase, const char *rand_str, uint32_t offset, uint32_t rand_id); + +uint32_t task_gen_get_n_randoms(struct task_base *tbase); +uint32_t task_gen_get_n_values(struct task_base *tbase); + +#endif /* _HANDLE_GEN_H_ */ diff --git a/VNFs/DPPD-PROX/handle_genl4.c b/VNFs/DPPD-PROX/handle_genl4.c new file mode 100644 index 00000000..4c62c641 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_genl4.c @@ -0,0 +1,1139 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <pcap.h> +#include <string.h> +#include <stdlib.h> +#include <rte_cycles.h> +#include <rte_version.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_tcp.h> +#include <rte_hash.h> +#include <rte_hash_crc.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" +#include "prox_malloc.h" +#include "file_utils.h" +#include "hash_set.h" +#include "prox_assert.h" +#include "prox_args.h" +#include "defines.h" +#include "pkt_parser.h" +#include "handle_lat.h" +#include "task_init.h" +#include "task_base.h" +#include "prox_port_cfg.h" +#include "lconf.h" +#include "log.h" +#include "quit.h" +#include "heap.h" +#include "mbuf_utils.h" +#include "genl4_bundle.h" +#include "genl4_stream_udp.h" +#include "genl4_stream_tcp.h" +#include "cdf.h" +#include "fqueue.h" +#include "token_time.h" +#include "commands.h" +#include "prox_shared.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +struct new_tuple { + uint32_t dst_addr; + uint8_t proto_id; + uint16_t dst_port; + uint16_t l2_types[4]; +} __attribute__((packed)); + +enum handle_state {HANDLE_QUEUED, HANDLE_SCHEDULED}; + +struct task_gen_server { + struct task_base base; + struct l4_stats l4_stats; + struct rte_mempool *mempool; + struct rte_hash *listen_hash; + /* Listening bundles contain only 1 part since the state of a + multi_part comm is kept mostly at the client side*/ + struct bundle_cfg **listen_entries; + struct bundle_ctx_pool bundle_ctx_pool; + struct bundle_cfg *bundle_cfgs; /* Loaded configurations */ + struct token_time token_time; + enum handle_state handle_state; + struct heap *heap; + struct fqueue *fqueue; + struct rte_mbuf *cur_mbufs[MAX_PKT_BURST]; + uint32_t cur_mbufs_beg; + uint32_t cur_mbufs_end; + uint32_t cancelled; + uint8_t out_saved; + struct rte_mbuf *mbuf_saved; + uint64_t last_tsc; + unsigned seed; + /* Handle scheduled events */ + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + uint32_t n_new_mbufs; +}; + +struct task_gen_client { + struct task_base base; + struct l4_stats l4_stats; + struct rte_mempool *mempool; + struct bundle_ctx_pool bundle_ctx_pool; + struct bundle_cfg *bundle_cfgs; /* Loaded configurations */ + struct token_time token_time; + /* Create new connections and handle scheduled events */ + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + uint32_t new_conn_cost; + uint32_t new_conn_tokens; + uint64_t new_conn_last_tsc; + uint32_t n_new_mbufs; + uint64_t last_tsc; + struct cdf *cdf; + unsigned seed; + struct heap *heap; +}; + +static int refill_mbufs(uint32_t *n_new_mbufs, struct rte_mempool *mempool, struct rte_mbuf **mbufs) +{ + if (*n_new_mbufs == MAX_PKT_BURST) + return 0; + + if (rte_mempool_get_bulk(mempool, (void **)mbufs, MAX_PKT_BURST - *n_new_mbufs) < 0) { + plogx_err("4Mempool alloc failed for %d mbufs\n", MAX_PKT_BURST - *n_new_mbufs); + return -1; + } + + for (uint32_t i = 0; i < MAX_PKT_BURST - *n_new_mbufs; ++i) { + init_mbuf_seg(mbufs[i]); + } + + *n_new_mbufs = MAX_PKT_BURST; + + return 0; +} + +static const struct bundle_cfg *server_accept(struct task_gen_server *task, struct new_tuple *nt) +{ + int ret = rte_hash_lookup(task->listen_hash, nt); + + if (ret < 0) + return NULL; + else + return task->listen_entries[ret]; +} + +static int handle_gen_bulk_client(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_gen_client *task = (struct task_gen_client *)tbase; + uint8_t out[MAX_PKT_BURST] = {0}; + struct bundle_ctx *conn; + int ret; + + if (n_pkts) { + for (int i = 0; i < n_pkts; ++i) { + struct pkt_tuple pt; + struct l4_meta l4_meta; + + if (parse_pkt(mbufs[i], &pt, &l4_meta)) { + plogdx_err(mbufs[i], "Parsing failed\n"); + out[i] = OUT_DISCARD; + continue; + } + + ret = rte_hash_lookup(task->bundle_ctx_pool.hash, (const void *)&pt); + + if (ret < 0) { + plogx_dbg("Client: packet RX that does not belong to connection:" + "Client = "IPv4_BYTES_FMT":%d, Server = "IPv4_BYTES_FMT":%d\n", + IPv4_BYTES(((uint8_t*)&pt.dst_addr)), + rte_bswap16(pt.dst_port), + IPv4_BYTES(((uint8_t*)&pt.src_addr)), + rte_bswap16(pt.src_port)); + + plogdx_dbg(mbufs[i], NULL); + + if (pt.proto_id == IPPROTO_TCP) { + stream_tcp_create_rst(mbufs[i], &l4_meta, &pt); + out[i] = 0; + continue; + } + else { + out[i] = OUT_DISCARD; + continue; + } + } + + conn = task->bundle_ctx_pool.hash_entries[ret]; + ret = bundle_proc_data(conn, mbufs[i], &l4_meta, &task->bundle_ctx_pool, &task->seed, &task->l4_stats); + out[i] = ret == 0? 0: OUT_HANDLED; + } + task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + } + + /* If there is at least one callback to handle, handle at most MAX_PKT_BURST */ + if (heap_top_is_lower(task->heap, rte_rdtsc())) { + if (0 != refill_mbufs(&task->n_new_mbufs, task->mempool, task->new_mbufs)) + return 0; + + uint16_t n_called_back = 0; + while (heap_top_is_lower(task->heap, rte_rdtsc()) && n_called_back < MAX_PKT_BURST) { + conn = BUNDLE_CTX_UPCAST(heap_pop(task->heap)); + + /* handle packet TX (retransmit or delayed transmit) */ + ret = bundle_proc_data(conn, task->new_mbufs[n_called_back], NULL, &task->bundle_ctx_pool, &task->seed, &task->l4_stats); + + if (ret == 0) { + out[n_called_back] = 0; + n_called_back++; + } + } + plogx_dbg("During callback, will send %d packets\n", n_called_back); + + task->base.tx_pkt(&task->base, task->new_mbufs, n_called_back, out); + task->n_new_mbufs -= n_called_back; + } + + uint32_t n_new = task->bundle_ctx_pool.n_free_bundles; + n_new = n_new > MAX_PKT_BURST? MAX_PKT_BURST : n_new; + + uint64_t diff = (rte_rdtsc() - task->new_conn_last_tsc)/task->new_conn_cost; + task->new_conn_last_tsc += diff * task->new_conn_cost; + task->new_conn_tokens += diff; + + if (task->new_conn_tokens > 16) + task->new_conn_tokens = 16; + if (n_new > task->new_conn_tokens) + n_new = task->new_conn_tokens; + task->new_conn_tokens -= n_new; + if (n_new == 0) + return 0; + + if (0 != refill_mbufs(&task->n_new_mbufs, task->mempool, task->new_mbufs)) + return 0; + + for (uint32_t i = 0; i < n_new; ++i) { + struct bundle_ctx *bundle_ctx = bundle_ctx_pool_get_w_cfg(&task->bundle_ctx_pool); + PROX_ASSERT(bundle_ctx); + + struct pkt_tuple *pt = &bundle_ctx->tuple; + + int n_retries = 0; + do { + /* Note that the actual packet sent will + contain swapped addresses and ports + (i.e. pkt.src <=> tuple.dst). The incoming + packet will match this struct. */ + bundle_init(bundle_ctx, task->heap, PEER_CLIENT, &task->seed); + + ret = rte_hash_lookup(task->bundle_ctx_pool.hash, (const void *)pt); + if (ret >= 0) { + if (n_retries++ == 1000) { + plogx_err("Already tried 1K times\n"); + } + } + } while (ret >= 0); + + ret = rte_hash_add_key(task->bundle_ctx_pool.hash, (const void *)pt); + + if (ret < 0) { + plogx_err("Failed to add key ret = %d, n_free = %d\n", ret, task->bundle_ctx_pool.n_free_bundles); + bundle_ctx_pool_put(&task->bundle_ctx_pool, bundle_ctx); + + pkt_tuple_debug2(pt); + out[i] = OUT_DISCARD; + continue; + } + + task->bundle_ctx_pool.hash_entries[ret] = bundle_ctx; + + if (bundle_ctx->ctx.stream_cfg->proto == IPPROTO_TCP) + task->l4_stats.tcp_created++; + else + task->l4_stats.udp_created++; + + task->l4_stats.bundles_created++; + + ret = bundle_proc_data(bundle_ctx, task->new_mbufs[i], NULL, &task->bundle_ctx_pool, &task->seed, &task->l4_stats); + out[i] = ret == 0? 0: OUT_HANDLED; + } + + int ret2 = task->base.tx_pkt(&task->base, task->new_mbufs, n_new, out); + task->n_new_mbufs -= n_new; + return ret2; +} + +static int handle_gen_queued(struct task_gen_server *task) +{ + uint8_t out[MAX_PKT_BURST]; + struct bundle_ctx *conn; + struct pkt_tuple pkt_tuple; + struct l4_meta l4_meta; + uint16_t j; + uint16_t cancelled = 0; + int ret; + + if (task->cur_mbufs_beg == task->cur_mbufs_end) { + task->cur_mbufs_end = fqueue_get(task->fqueue, task->cur_mbufs, MAX_PKT_BURST); + task->cur_mbufs_beg = 0; + } + uint16_t n_pkts = task->cur_mbufs_end - task->cur_mbufs_beg; + struct rte_mbuf **mbufs = task->cur_mbufs + task->cur_mbufs_beg; + + j = task->cancelled; + if (task->cancelled) { + uint16_t pkt_len = mbuf_wire_size(mbufs[0]); + + if (token_time_take(&task->token_time, pkt_len) != 0) + return -1; + + out[0] = task->out_saved; + task->cancelled = 0; + } + + /* Main proc loop */ + for (; j < n_pkts; ++j) { + + if (parse_pkt(mbufs[j], &pkt_tuple, &l4_meta)) { + plogdx_err(mbufs[j], "Unknown packet, parsing failed\n"); + out[j] = OUT_DISCARD; + } + + conn = NULL; + ret = rte_hash_lookup(task->bundle_ctx_pool.hash, (const void *)&pkt_tuple); + + if (ret >= 0) + conn = task->bundle_ctx_pool.hash_entries[ret]; + else { + /* If not part of existing connection, try to create a connection */ + struct new_tuple nt; + nt.dst_addr = pkt_tuple.dst_addr; + nt.proto_id = pkt_tuple.proto_id; + nt.dst_port = pkt_tuple.dst_port; + rte_memcpy(nt.l2_types, pkt_tuple.l2_types, sizeof(nt.l2_types)); + const struct bundle_cfg *n; + + if (NULL != (n = server_accept(task, &nt))) { + conn = bundle_ctx_pool_get(&task->bundle_ctx_pool); + if (!conn) { + out[j] = OUT_DISCARD; + plogx_err("No more free bundles to accept new connection\n"); + continue; + } + ret = rte_hash_add_key(task->bundle_ctx_pool.hash, (const void *)&pkt_tuple); + if (ret < 0) { + out[j] = OUT_DISCARD; + bundle_ctx_pool_put(&task->bundle_ctx_pool, conn); + plog_err("Adding key failed while trying to accept connection\n"); + continue; + } + + task->bundle_ctx_pool.hash_entries[ret] = conn; + + bundle_init_w_cfg(conn, n, task->heap, PEER_SERVER, &task->seed); + conn->tuple = pkt_tuple; + + if (conn->ctx.stream_cfg->proto == IPPROTO_TCP) + task->l4_stats.tcp_created++; + else + task->l4_stats.udp_created++; + } + else { + plog_err("Packet received for service that does not exist :\n" + "source ip = %0x:%u\n" + "dst ip = %0x:%u\n", + pkt_tuple.src_addr, rte_bswap16(pkt_tuple.src_port), + pkt_tuple.dst_addr, rte_bswap16(pkt_tuple.dst_port)); + } + } + + /* bundle contains either an active connection or a + newly created connection. If it is NULL, then not + listening. */ + if (NULL != conn) { + ret = bundle_proc_data(conn, mbufs[j], &l4_meta, &task->bundle_ctx_pool, &task->seed, &task->l4_stats); + + out[j] = ret == 0? 0: OUT_HANDLED; + + if (ret == 0) { + uint16_t pkt_len = mbuf_wire_size(mbufs[j]); + + if (token_time_take(&task->token_time, pkt_len) != 0) { + task->out_saved = out[j]; + task->cancelled = 1; + task->base.tx_pkt(&task->base, mbufs, j, out); + task->cur_mbufs_beg += j; + return -1; + } + } + } + else { + pkt_tuple_debug(&pkt_tuple); + plogd_dbg(mbufs[j], NULL); + out[j] = OUT_DISCARD; + } + } + + task->base.tx_pkt(&task->base, mbufs, j, out); + + task->cur_mbufs_beg += j; + return 0; +} + +static int handle_gen_scheduled(struct task_gen_server *task) +{ + struct bundle_ctx *conn; + uint8_t out[MAX_PKT_BURST]; + int ret; + uint16_t n_called_back = 0; + + if (task->cancelled) { + struct rte_mbuf *mbuf = task->mbuf_saved; + + uint16_t pkt_len = mbuf_wire_size(mbuf); + if (token_time_take(&task->token_time, pkt_len) == 0) { + task->cancelled = 0; + out[0] = 0; + task->base.tx_pkt(&task->base, &mbuf, 1, out); + } + else { + return -1; + } + } + + if (0 != refill_mbufs(&task->n_new_mbufs, task->mempool, task->new_mbufs)) + return -1; + + conn = NULL; + while (heap_top_is_lower(task->heap, rte_rdtsc()) && n_called_back < task->n_new_mbufs) { + conn = BUNDLE_CTX_UPCAST(heap_pop(task->heap)); + + /* handle packet TX (retransmit or delayed transmit) */ + ret = bundle_proc_data(conn, task->new_mbufs[n_called_back], NULL, &task->bundle_ctx_pool, &task->seed, &task->l4_stats); + + if (ret == 0) { + struct rte_mbuf *mbuf = task->new_mbufs[n_called_back]; + uint16_t pkt_len = mbuf_wire_size(mbuf); + + if (token_time_take(&task->token_time, pkt_len) == 0) { + out[n_called_back] = 0; + n_called_back++; + } + else { + + struct ether_hdr *eth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct ipv4_hdr *ip = (struct ipv4_hdr*)(eth + 1); + struct tcp_hdr *tcp = (struct tcp_hdr*)(ip + 1); + + task->out_saved = 0; + task->cancelled = 1; + task->mbuf_saved = mbuf; + task->base.tx_pkt(&task->base, task->new_mbufs, n_called_back, out); + /* The mbuf that is currently been + processed (and which has been + cancelled) is saved in + task->mbuf_saved. It will be + restored as the first mbuf when + this function is called again. */ + task->n_new_mbufs -= (n_called_back + 1); + return -1; + } + } + } + + task->base.tx_pkt(&task->base, task->new_mbufs, n_called_back, out); + task->n_new_mbufs -= n_called_back; + + return 0; +} + +static int handle_gen_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_gen_server *task = (struct task_gen_server *)tbase; + struct bundle_ctx *conn; + int ret, ret2 = 0; + + token_time_update(&task->token_time, rte_rdtsc()); + + if ((ret = fqueue_put(task->fqueue, mbufs, n_pkts)) != n_pkts) { + uint8_t out[MAX_PKT_BURST]; + for (uint16_t j = 0; j < n_pkts - ret; ++j) + out[j] = OUT_DISCARD; + + ret2 = task->base.tx_pkt(&task->base, mbufs + ret, n_pkts - ret, out); + } + if (task->handle_state == HANDLE_QUEUED) { + if (handle_gen_queued(task) == 0) { + if (handle_gen_scheduled(task) != 0) + task->handle_state = HANDLE_SCHEDULED; + } + } + else { + if (handle_gen_scheduled(task) == 0) { + if (handle_gen_queued(task) != 0) + task->handle_state = HANDLE_QUEUED; + } + } + return ret2; +} + +static int lua_to_host_set(struct lua_State *L, enum lua_place from, const char *name, struct host_set *h) +{ + int pop; + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) + return -1; + + uint32_t port = 0, port_mask = 0; + + if (lua_to_ip(L, TABLE, "ip", &h->ip) || lua_to_int(L, TABLE, "port", &port)) + return -1; + + if (lua_to_int(L, TABLE, "ip_mask", &h->ip_mask)) + h->ip_mask = 0; + if (lua_to_int(L, TABLE, "port_mask", &port_mask)) + h->port_mask = 0; + + h->port = rte_bswap16(port); + h->port_mask = rte_bswap16(port_mask); + h->ip = rte_bswap32(h->ip); + h->ip_mask = rte_bswap32(h->ip_mask); + + lua_pop(L, pop); + return 0; +} + +static int file_read_cached(const char *file_name, uint8_t **mem, uint32_t beg, uint32_t len, uint32_t socket, struct hash_set *hs) +{ + if (len == 0) { + *mem = 0; + return 0; + } + + uint8_t *data_mem; + + /* Since the configuration can reference the same file from + multiple places, use prox_shared infrastructure to detect + this and return previously loaded data. */ + char name[256]; + + snprintf(name, sizeof(name), "%u-%u:%s", beg, len, file_name); + *mem = prox_sh_find_socket(socket, name); + if (*mem) + return 0; + + /* check if the file has been loaded on the other socket. */ + if (socket == 1 && (data_mem = prox_sh_find_socket(0, name))) { + uint8_t *data_find = hash_set_find(hs, data_mem, len); + if (!data_find) { + data_find = prox_zmalloc(len, socket); + PROX_PANIC(data_find == NULL, "Failed to allocate memory (%u bytes) to hold header for peer\n", len); + + rte_memcpy(data_find, data_mem, len); + hash_set_add(hs, data_find, len); + } + *mem = data_find; + prox_sh_add_socket(socket, name, *mem); + return 0; + } + + /* It is possible that a file with a different name contains + the same data. In that case, search all loaded files and + compare the data to reduce memory utilization.*/ + data_mem = malloc(len); + PROX_PANIC(data_mem == NULL, "Failed to allocate temporary memory to hold data\n"); + + if (file_read_content(file_name, data_mem, beg, len)) { + plog_err("%s\n", file_get_error()); + return -1; + } + + uint8_t *data_find = hash_set_find(hs, data_mem, len); + if (!data_find) { + data_find = prox_zmalloc(len, socket); + PROX_PANIC(data_find == NULL, "Failed to allocate memory (%u bytes) to hold header for peer\n", len); + + rte_memcpy(data_find, data_mem, len); + hash_set_add(hs, data_find, len); + } + + free(data_mem); + + *mem = data_find; + prox_sh_add_socket(socket, name, *mem); + return 0; +} + +static int lua_to_peer_data(struct lua_State *L, enum lua_place from, const char *name, uint32_t socket, struct peer_data *peer_data, size_t *cl, struct hash_set *hs) +{ + uint32_t hdr_len, hdr_beg, content_len, content_beg; + char hdr_file[256], content_file[256]; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) + return -1; + + if (lua_getfrom(L, TABLE, "header") < 0) + return -1; + if (lua_to_int(L, TABLE, "len", &hdr_len) < 0) + return -1; + if (lua_to_int(L, TABLE, "beg", &hdr_beg) < 0) + return -1; + if (lua_to_string(L, TABLE, "file_name", hdr_file, sizeof(hdr_file)) < 0) + return -1; + lua_pop(L, 1); + + if (lua_getfrom(L, TABLE, "content") < 0) + return -1; + if (lua_to_int(L, TABLE, "len", &content_len) < 0) + return -1; + if (lua_to_int(L, TABLE, "beg", &content_beg) < 0) + return -1; + if (lua_to_string(L, TABLE, "file_name", content_file, sizeof(content_file)) < 0) + return -1; + lua_pop(L, 1); + + if (hdr_len == UINT32_MAX) { + long ret = file_get_size(hdr_file); + + if (ret < 0) { + plog_err("%s", file_get_error()); + return -1; + } + hdr_len = ret - hdr_beg; + } + + if (content_len == UINT32_MAX) { + long ret = file_get_size(content_file); + + if (ret < 0) { + plog_err("%s", file_get_error()); + return -1; + } + content_len = ret - content_beg; + } + *cl = content_len; + peer_data->hdr_len = hdr_len; + + if (file_read_cached(hdr_file, &peer_data->hdr, hdr_beg, hdr_len, socket, hs)) + return -1; + if (file_read_cached(content_file, &peer_data->content, content_beg, content_len, socket, hs)) + return -1; + + lua_pop(L, pop); + return 0; +} + +static int lua_to_peer_action(struct lua_State *L, enum lua_place from, const char *name, struct peer_action *action, size_t client_contents_len, size_t server_contents_len) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) + return -1; + + uint32_t peer, beg, len; + if (lua_to_int(L, TABLE, "peer", &peer) || + lua_to_int(L, TABLE, "beg", &beg) || + lua_to_int(L, TABLE, "len", &len)) { + return -1; + } + size_t data_len = (peer == PEER_CLIENT? client_contents_len : server_contents_len); + if (len == (uint32_t)-1) + len = data_len - beg; + + PROX_PANIC(beg + len > data_len, "Accessing data past the end (starting at %u for %u bytes) while total length is %zu\n", beg, len, data_len); + + action->peer = peer; + action->beg = beg; + action->len = len; + lua_pop(L, pop); + return 0; +} + +static int lua_to_stream_cfg(struct lua_State *L, enum lua_place from, const char *name, uint32_t socket, struct stream_cfg **stream_cfg, struct hash_set *hs) +{ + int pop; + struct stream_cfg *ret; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (lua_getfrom(L, TABLE, "actions") < 0) + return -1; + + lua_len(prox_lua(), -1); + uint32_t n_actions = lua_tointeger(prox_lua(), -1); + lua_pop(prox_lua(), 1); + + lua_pop(L, 1); + + size_t mem_size = 0; + mem_size += sizeof(*ret); + /* one additional action is allocated to allow inserting an + additional "default" action to close down TCP sessions from + the client side. */ + mem_size += sizeof(ret->actions[0]) * (n_actions + 1); + + ret = prox_zmalloc(sizeof(*ret) + mem_size, socket); + ret->n_actions = n_actions; + + size_t client_contents_len, server_contents_len; + char proto[16]; + uint32_t timeout_us, timeout_time_wait_us; + plogx_dbg("loading stream\n"); + if (lua_to_host_set(L, TABLE, "servers", &ret->servers)) + return -1; + if (lua_to_string(L, TABLE, "l4_proto", proto, sizeof(proto))) + return -1; + if (lua_to_peer_data(L, TABLE, "client_data", socket, &ret->data[PEER_CLIENT], &client_contents_len, hs)) + return -1; + if (lua_to_peer_data(L, TABLE, "server_data", socket, &ret->data[PEER_SERVER], &server_contents_len, hs)) + return -1; + + if (lua_to_int(L, TABLE, "timeout", &timeout_us)) { + timeout_us = 1000000; + } + + ret->tsc_timeout = usec_to_tsc(timeout_us); + + double up, dn; + + if (lua_to_double(L, TABLE, "up_bps", &up)) + up = 5000;// Default rate is 40 Mbps + + if (lua_to_double(L, TABLE, "dn_bps", &dn)) + dn = 5000;// Default rate is 40 Mbps + + const uint64_t hz = rte_get_tsc_hz(); + + ret->tt_cfg[PEER_CLIENT] = token_time_cfg_create(up, hz, ETHER_MAX_LEN + 20); + ret->tt_cfg[PEER_SERVER] = token_time_cfg_create(dn, hz, ETHER_MAX_LEN + 20); + + if (!strcmp(proto, "tcp")) { + ret->proto = IPPROTO_TCP; + ret->proc = stream_tcp_proc; + ret->is_ended = stream_tcp_is_ended; + + if (lua_to_int(L, TABLE, "timeout_time_wait", &timeout_time_wait_us)) { + timeout_time_wait_us = 2000000; + } + + ret->tsc_timeout_time_wait = usec_to_tsc(timeout_time_wait_us); + } + else if (!strcmp(proto, "udp")) { + plogx_dbg("loading UDP\n"); + ret->proto = IPPROTO_UDP; + ret->proc = stream_udp_proc; + ret->is_ended = stream_udp_is_ended; + } + else + return -1; + + /* get all actions */ + if (lua_getfrom(L, TABLE, "actions") < 0) + return -1; + + uint32_t idx = 0; + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_peer_action(L, STACK, NULL, &ret->actions[idx], client_contents_len, server_contents_len)) + return -1; + + stream_cfg_verify_action(ret, &ret->actions[idx]); + + idx++; + + lua_pop(L, 1); + } + lua_pop(L, 1); + + /* For TCP, one of the peers initiates closing down the + connection. This is signified by the last action having + with zero length. If such an action is not specified in the + configuration file, the default is for the client to close + the connection. This means that the TCP connection at the + client will go into a TIME_WAIT state and the server + releases all the resources avoiding resource starvation at + the server. */ + if (ret->proto == IPPROTO_TCP && ret->actions[ret->n_actions - 1].len != 0) { + ret->actions[ret->n_actions].len = 0; + ret->actions[ret->n_actions].beg = 0; + ret->actions[ret->n_actions].peer = PEER_CLIENT; + ret->n_actions++; + } + + if (IPPROTO_TCP == ret->proto) + stream_tcp_calc_len(ret, &ret->n_pkts, &ret->n_bytes); + else + stream_udp_calc_len(ret, &ret->n_pkts, &ret->n_bytes); + + lua_pop(L, pop); + *stream_cfg = ret; + return 0; +} + +static int lua_to_bundle_cfg(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct bundle_cfg *bundle, struct hash_set *hs) +{ + int pop, pop2, idx; + int clients_loaded = 0; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) + return -1; + + lua_len(prox_lua(), -1); + bundle->n_stream_cfgs = lua_tointeger(prox_lua(), -1); + lua_pop(prox_lua(), 1); + + bundle->stream_cfgs = prox_zmalloc(sizeof(*bundle->stream_cfgs) * bundle->n_stream_cfgs, socket); + + plogx_dbg("loading bundle cfg with %d streams\n", bundle->n_stream_cfgs); + idx = 0; + lua_pushnil(L); + while (lua_next(L, -2)) { + if (!clients_loaded) { + if (lua_to_host_set(L, TABLE, "clients", &bundle->clients)) { + return -1; + } + clients_loaded = 1; + } + if (lua_to_stream_cfg(L, STACK, NULL, socket, &bundle->stream_cfgs[idx], hs)) { + return -1; + } + + ++idx; + lua_pop(L, 1); + } + + lua_pop(L, pop); + return 0; +} + +static void init_task_gen(struct task_base *tbase, struct task_args *targ) +{ + struct task_gen_server *task = (struct task_gen_server *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + static char name[] = "server_mempool"; + name[0]++; + task->mempool = rte_mempool_create(name, + 4*1024 - 1, MBUF_SIZE, + targ->nb_cache_mbuf, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, 0, + socket_id, 0); + PROX_PANIC(task->mempool == NULL, "Failed to allocate memory pool with %u elements\n", 4*1024 - 1); + int pop = lua_getfrom(prox_lua(), GLOBAL, targ->streams); + PROX_PANIC(pop < 0, "Failed to find '%s' in lua\n", targ->streams); + + lua_len(prox_lua(), -1); + uint32_t n_listen = lua_tointeger(prox_lua(), -1); + lua_pop(prox_lua(), 1); + PROX_PANIC(n_listen == 0, "No services specified to listen on\n"); + + task->bundle_cfgs = prox_zmalloc(n_listen * sizeof(task->bundle_cfgs[0]), socket_id); + + plogx_info("n_listen = %d\n", n_listen); + + struct hash_set *hs = prox_sh_find_socket(socket_id, "genl4_streams"); + if (hs == NULL) { + /* Expected number of streams per bundle = 1, hash_set + will grow if full. */ + hs = hash_set_create(n_listen, socket_id); + prox_sh_add_socket(socket_id, "genl4_streams", hs); + } + + const struct rte_hash_parameters listen_table = { + .name = name, + .entries = n_listen * 4, + .key_len = sizeof(struct new_tuple), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + .socket_id = socket_id, + }; + name[0]++; + + task->listen_hash = rte_hash_create(&listen_table); + task->listen_entries = prox_zmalloc(listen_table.entries * sizeof(task->listen_entries[0]), socket_id); + + int idx = 0; + lua_pushnil(prox_lua()); + while (lua_next(prox_lua(), -2)) { + task->bundle_cfgs[idx].n_stream_cfgs = 1; + task->bundle_cfgs[idx].stream_cfgs = prox_zmalloc(sizeof(*task->bundle_cfgs[idx].stream_cfgs), socket_id); + int ret = lua_to_stream_cfg(prox_lua(), STACK, NULL, socket_id, &task->bundle_cfgs[idx].stream_cfgs[0], hs); + PROX_PANIC(ret, "Failed to load stream cfg\n"); + struct stream_cfg *stream = task->bundle_cfgs[idx].stream_cfgs[0]; + + // TODO: check mask and add to hash for each host + struct new_tuple nt = { + .dst_addr = stream->servers.ip, + .proto_id = stream->proto, + .dst_port = stream->servers.port, + .l2_types[0] = 0x0008, + }; + + ret = rte_hash_add_key(task->listen_hash, &nt); + PROX_PANIC(ret < 0, "Failed to add\n"); + + task->listen_entries[ret] = &task->bundle_cfgs[idx]; + + plogx_dbg("Server = "IPv4_BYTES_FMT":%d\n", IPv4_BYTES(((uint8_t*)&nt.dst_addr)), rte_bswap16(nt.dst_port)); + ++idx; + lua_pop(prox_lua(), 1); + } + + static char name2[] = "task_gen_hash2"; + + name2[0]++; + plogx_dbg("Creating bundle ctx pool\n"); + if (bundle_ctx_pool_create(name2, targ->n_concur_conn * 2, &task->bundle_ctx_pool, NULL, 0, NULL, socket_id)) { + cmd_mem_stats(); + PROX_PANIC(1, "Failed to create conn_ctx_pool\n"); + } + + task->heap = heap_create(targ->n_concur_conn * 2, socket_id); + task->seed = rte_rdtsc(); + + /* TODO: calculate the CDF of the reply distribution and the + number of replies as the number to cover for 99% of the + replies. For now, assume that this is number is 2. */ + uint32_t queue_size = rte_align32pow2(targ->n_concur_conn * 2); + + PROX_PANIC(queue_size == 0, "Overflow resulted in queue size 0\n"); + task->fqueue = fqueue_create(queue_size, socket_id); + PROX_PANIC(task->fqueue == NULL, "Failed to allocate local queue\n"); + + uint32_t n_descriptors; + + if (targ->nb_txports) { + PROX_PANIC(targ->nb_txports != 1, "Need exactly one TX port for L4 generation\n"); + n_descriptors = prox_port_cfg[targ->tx_port_queue[0].port].n_txd; + } else { + PROX_PANIC(targ->nb_txrings != 1, "Need exactly one TX ring for L4 generation\n"); + n_descriptors = 256; + } + + struct token_time_cfg tt_cfg = { + .bpp = targ->rate_bps, + .period = rte_get_tsc_hz(), + .bytes_max = n_descriptors * (ETHER_MIN_LEN + 20), + }; + + token_time_init(&task->token_time, &tt_cfg); +} + +static void init_task_gen_client(struct task_base *tbase, struct task_args *targ) +{ + struct task_gen_client *task = (struct task_gen_client *)tbase; + static char name[] = "gen_pool"; + const uint32_t socket = rte_lcore_to_socket_id(targ->lconf->id); + name[0]++; + task->mempool = rte_mempool_create(name, + 4*1024 - 1, MBUF_SIZE, + targ->nb_cache_mbuf, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, 0, + socket, 0); + PROX_PANIC(task->mempool == NULL, "Failed to allocate memory pool with %u elements\n", 4*1024 - 1); + + /* streams contains a lua table. Go through it and read each + stream with associated imix_fraction. */ + uint32_t imix; + uint32_t i = 0; + + int pop = lua_getfrom(prox_lua(), GLOBAL, targ->streams); + PROX_PANIC(pop < 0, "Failed to find '%s' in lua\n", targ->streams); + + lua_len(prox_lua(), -1); + uint32_t n_bundle_cfgs = lua_tointeger(prox_lua(), -1); + lua_pop(prox_lua(), 1); + PROX_PANIC(n_bundle_cfgs == 0, "No configs specified\n"); + plogx_info("loading %d bundle_cfgs\n", n_bundle_cfgs); + + struct hash_set *hs = prox_sh_find_socket(socket, "genl4_streams"); + if (hs == NULL) { + /* Expected number of streams per bundle = 8, hash_set + will grow if full. */ + hs = hash_set_create(n_bundle_cfgs * 8, socket); + prox_sh_add_socket(socket, "genl4_streams", hs); + } + + task->bundle_cfgs = prox_zmalloc(n_bundle_cfgs * sizeof(task->bundle_cfgs[0]), socket); + lua_pushnil(prox_lua()); + + int total_imix = 0; + + uint32_t *occur = prox_zmalloc(n_bundle_cfgs * sizeof(*occur), socket); + struct cdf *cdf = cdf_create(n_bundle_cfgs, socket); + + while (lua_next(prox_lua(), -2)) { + PROX_PANIC(lua_to_int(prox_lua(), TABLE, "imix_fraction", &imix) || + lua_to_bundle_cfg(prox_lua(), TABLE, "bundle", socket, &task->bundle_cfgs[i], hs), + "Failed to load bundle cfg:\n%s\n", get_lua_to_errors()); + cdf_add(cdf, imix); + occur[i] = imix; + total_imix += imix; + ++i; + lua_pop(prox_lua(), 1); + } + + lua_pop(prox_lua(), pop); + cdf_setup(cdf); + + PROX_PANIC(targ->max_setup_rate == 0, "Max setup rate not set\n"); + + task->new_conn_cost = rte_get_tsc_hz()/targ->max_setup_rate; + + static char name2[] = "task_gen_hash"; + name2[0]++; + plogx_dbg("Creating bundle ctx pool\n"); + if (bundle_ctx_pool_create(name2, targ->n_concur_conn, &task->bundle_ctx_pool, occur, n_bundle_cfgs, task->bundle_cfgs, socket)) { + cmd_mem_stats(); + PROX_PANIC(1, "Failed to create conn_ctx_pool\n"); + } + + task->heap = heap_create(targ->n_concur_conn, socket); + task->seed = rte_rdtsc(); + /* task->token_time.bytes_max = MAX_PKT_BURST * (ETHER_MAX_LEN + 20); */ + + /* To avoid overflowing the tx descriptors, the token bucket + size needs to be limited. The descriptors are filled most + quickly with the smallest packets. For that reason, the + token bucket size is given by "number of tx descriptors" * + "smallest Ethernet packet". */ + PROX_ASSERT(targ->nb_txports == 1); + + struct token_time_cfg tt_cfg = { + .bpp = targ->rate_bps, + .period = rte_get_tsc_hz(), + .bytes_max = prox_port_cfg[targ->tx_port_queue[0].port].n_txd * (ETHER_MIN_LEN + 20), + }; + + token_time_init(&task->token_time, &tt_cfg); +} + +static void start_task_gen_client(struct task_base *tbase) +{ + struct task_gen_client *task = (struct task_gen_client *)tbase; + + token_time_reset(&task->token_time, rte_rdtsc(), 0); + + task->new_conn_tokens = 0; + task->new_conn_last_tsc = rte_rdtsc(); +} + +static void stop_task_gen_client(struct task_base *tbase) +{ + struct task_gen_client *task = (struct task_gen_client *)tbase; + struct bundle_ctx *bundle; + + while (!heap_is_empty(task->heap)) { + bundle = BUNDLE_CTX_UPCAST(heap_pop(task->heap)); + bundle_expire(bundle, &task->bundle_ctx_pool, &task->l4_stats); + } +} + +static void start_task_gen_server(struct task_base *tbase) +{ + struct task_gen_server *task = (struct task_gen_server *)tbase; + + token_time_reset(&task->token_time, rte_rdtsc(), 0); +} + +static void stop_task_gen_server(struct task_base *tbase) +{ + struct task_gen_server *task = (struct task_gen_server *)tbase; + struct bundle_ctx *bundle; + uint8_t out[MAX_PKT_BURST]; + + while (!heap_is_empty(task->heap)) { + bundle = BUNDLE_CTX_UPCAST(heap_pop(task->heap)); + bundle_expire(bundle, &task->bundle_ctx_pool, &task->l4_stats); + } + + if (task->cancelled) { + struct rte_mbuf *mbuf = task->mbuf_saved; + + out[0] = OUT_DISCARD; + task->cancelled = 0; + task->base.tx_pkt(&task->base, &mbuf, 1, out); + } + + do { + if (task->cur_mbufs_beg == task->cur_mbufs_end) { + task->cur_mbufs_end = fqueue_get(task->fqueue, task->cur_mbufs, MAX_PKT_BURST); + task->cur_mbufs_beg = 0; + if (task->cur_mbufs_end == 0) + break; + } + uint16_t n_pkts = task->cur_mbufs_end - task->cur_mbufs_beg; + struct rte_mbuf **mbufs = task->cur_mbufs + task->cur_mbufs_beg; + + if (n_pkts) { + for (uint16_t j = 0; j < n_pkts; ++j) { + out[j] = OUT_DISCARD; + } + task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + } + } while (1); +} + +static struct task_init task_init_gen1 = { + .mode_str = "genl4", + .sub_mode_str = "server", + .init = init_task_gen, + .handle = handle_gen_bulk, + .start = start_task_gen_server, + .stop = stop_task_gen_server, + .flag_features = TASK_FEATURE_ZERO_RX, + .size = sizeof(struct task_gen_server), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +static struct task_init task_init_gen2 = { + .mode_str = "genl4", + .init = init_task_gen_client, + .handle = handle_gen_bulk_client, + .start = start_task_gen_client, + .stop = stop_task_gen_client, + .flag_features = TASK_FEATURE_ZERO_RX, + .size = sizeof(struct task_gen_client), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_gen(void) +{ + reg_task(&task_init_gen1); + reg_task(&task_init_gen2); +} diff --git a/VNFs/DPPD-PROX/handle_gre_decap_encap.c b/VNFs/DPPD-PROX/handle_gre_decap_encap.c new file mode 100644 index 00000000..41f6dd33 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_gre_decap_encap.c @@ -0,0 +1,462 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_byteorder.h> +#include <rte_cycles.h> +#include <rte_hash.h> +#include <rte_ip.h> + +#include "prox_malloc.h" +#include "task_init.h" +#include "lconf.h" +#include "defines.h" +#include "stats.h" +#include "tx_pkt.h" +#include "hash_entry_types.h" +#include "prefetch.h" +#include "prox_cksum.h" +#include "gre.h" +#include "etypes.h" +#include "log.h" +#include "quit.h" +#include "prox_assert.h" +#include "pkt_prototypes.h" +#include "quit.h" + +struct cpe_gre_key { + struct ether_addr clt_mac; + uint16_t pad; +} __attribute__((__packed__)); + +struct cpe_gre_data { + uint32_t gre_id; + uint32_t cpe_ip; + uint64_t tsc; +#ifdef GRE_TP + uint64_t tp_tsc; + double tp_tbsize; +#endif +} __attribute__((__packed__)); + +struct task_gre_decap { + struct task_base base; + struct rte_hash *cpe_gre_hash; + struct cpe_gre_data *cpe_gre_data; + struct lcore_cfg *lconf; + uint8_t runtime_flags; + uint8_t mapping[PROX_MAX_PORTS]; + uint32_t bucket_index; + int offload_crc; + const void* key_ptr[16]; + struct cpe_gre_key key[16]; + uint64_t cpe_timeout; +#ifdef GRE_TP + double cycles_per_byte; + uint32_t tb_size; +#endif +}; + +static void handle_gre_decap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +static void handle_gre_encap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + +static inline uint8_t handle_gre_encap(struct task_gre_decap *task, struct rte_mbuf *mbuf, struct cpe_gre_data *table); +static inline void handle_gre_encap16(struct task_gre_decap *task, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +static inline uint8_t handle_gre_decap(struct task_gre_decap *tbase, struct rte_mbuf *mbuf); + +void update_arp_entries_gre(void *data); + +static void init_cpe_gre_hash(struct task_args *targ) +{ + char name[64]; + uint8_t socket_id; + uint8_t lcore_id; + uint8_t table_part; + + /* Already set up by other task */ + if (targ->cpe_gre_hash) { + return; + } + + lcore_id = targ->lconf->id; + socket_id = rte_lcore_to_socket_id(lcore_id); + sprintf(name, "core_%u_CPE_GRE_Table", targ->lconf->id); + table_part = targ->nb_slave_threads; + + if (table_part == 0) + table_part = 1; + if (!rte_is_power_of_2(table_part)) { + table_part = rte_align32pow2(table_part) >> 1; + } + + struct rte_hash_parameters hash_params = { + .name = name, + .entries = MAX_GRE / table_part, + .bucket_entries = GRE_BUCKET_ENTRIES, + .key_len = sizeof(struct cpe_gre_key), + .hash_func_init_val = 0, + .socket_id = socket_id + }; + + struct rte_hash* phash = rte_hash_create(&hash_params); + struct cpe_gre_data *cpe_gre_data = prox_zmalloc(MAX_GRE / table_part, socket_id); + + PROX_PANIC(phash == NULL, "Unable to allocate memory for IPv4 hash table on core %u\n", lcore_id); + + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (smode == GRE_DECAP || smode == GRE_ENCAP) { + targ->lconf->targs[task_id].cpe_gre_hash = phash; + targ->lconf->targs[task_id].cpe_gre_data = cpe_gre_data; + } + } +} + +static void init_task_gre_decap(struct task_base *tbase, struct task_args *targ) +{ + struct task_gre_decap *task = (struct task_gre_decap *)tbase; + + init_cpe_gre_hash(targ); + task->cpe_gre_hash = targ->cpe_gre_hash; + task->cpe_gre_data = targ->cpe_gre_data; + task->runtime_flags = targ->runtime_flags; + task->lconf = targ->lconf; + task->cpe_timeout = msec_to_tsc(targ->cpe_table_timeout_ms); + + targ->lconf->period_func = update_arp_entries_gre; + targ->lconf->period_data = tbase; + targ->lconf->period_timeout = msec_to_tsc(500) / NUM_VCPES; + + for (uint8_t i = 0; i < 16; ++i) { + task->key_ptr[i] = &task->key[i]; + } +} + +static void init_task_gre_encap(struct task_base *tbase, struct task_args *targ) +{ + struct task_gre_decap *task = (struct task_gre_decap *)tbase; + + init_cpe_gre_hash(targ); + task->cpe_gre_hash = targ->cpe_gre_hash; + task->cpe_gre_data = targ->cpe_gre_data; + task->runtime_flags = targ->runtime_flags; + task->lconf = targ->lconf; + + struct port_cfg *port = find_reachable_task_sending_to_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } + +#ifdef GRE_TP + if (targ->tb_rate) { + task->cycles_per_byte = ((double)rte_get_tsc_hz()) / ((double)targ->tb_rate); + task->tb_size = targ->tb_size != 0 ? targ->tb_size : 1520; + } + else { + /* traffic policing disabled */ + task->cycles_per_byte = 0; + } +#endif +} + +static struct task_init task_init_gre_decap = { + .mode = GRE_DECAP, + .mode_str = "gredecap", + .init = init_task_gre_decap, + .handle = handle_gre_decap_bulk, + .size = sizeof(struct task_gre_decap) +}; + +static struct task_init task_init_gre_encap = { + .mode = GRE_ENCAP, + .mode_str = "greencap", + .init = init_task_gre_encap, + .handle = handle_gre_encap_bulk, + .size = sizeof(struct task_gre_decap) +}; + +__attribute__((constructor)) static void reg_task_gre(void) +{ + reg_task(&task_init_gre_decap); + reg_task(&task_init_gre_encap); +} + +void handle_gre_decap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_gre_decap *task = (struct task_gre_decap *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_gre_decap(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_gre_decap(task, mbufs[j]); + } +#endif + + task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +struct gre_packet { + struct ether_hdr eth; + struct ipv4_hdr ip; + struct gre_hdr gre; + union { + struct ether_hdr eth2; + struct ipv4_hdr ip2; + }; +} __attribute__((__packed__)); + +/* Handle ipv4 over GRE and Ethernet over GRE. In case of ipv4 over + GRE remove gre and ipv4 header and retain space for ethernet + header. In case of Eth over GRE remove external eth, gre and ipv4 + headers and return pointer to payload */ +static inline struct ether_hdr *gre_decap(struct gre_hdr *pgre, struct rte_mbuf *mbuf) +{ + int16_t hsize = 0; + if (pgre->type == ETYPE_EoGRE) { + hsize = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr); + } + else if (pgre->type == ETYPE_IPv4) { + /* retain sizeof(struct ether_hdr) */ + hsize = sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr); + } + else { + return NULL; + } + + return (struct ether_hdr *)rte_pktmbuf_adj(mbuf, hsize); +} + +static inline uint8_t handle_gre_decap(struct task_gre_decap *task, struct rte_mbuf *mbuf) +{ + struct ipv4_hdr *pip = (struct ipv4_hdr *)(rte_pktmbuf_mtod(mbuf, struct ether_hdr *) + 1); + + if (pip->next_proto_id != IPPROTO_GRE) { + plog_warn("Invalid packet proto_id = 0x%x expect 0x%x\n", + pip->next_proto_id, IPPROTO_GRE); + return OUT_DISCARD; + } + + struct cpe_gre_data data; + struct cpe_gre_key key; + struct gre_hdr *pgre = (struct gre_hdr *)(pip + 1); + data.gre_id = pgre->gre_id; + data.cpe_ip = pip->src_addr; + + struct ether_hdr *peth = gre_decap(pgre, mbuf); + PROX_PANIC(peth != 0, "Failed to gre_decap"); + + pip = (struct ipv4_hdr *)(peth + 1); + +/* emulate client MAC for test purposes */ +#if 1 + if (pgre->type == ETYPE_IPv4) { + struct ether_hdr eth = { + .d_addr = {.addr_bytes = + {0x0A, 0x02, 0x0A, 0x0A, 0x00, 0x01}}, + .s_addr = {.addr_bytes = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .ether_type = ETYPE_IPv4 + }; + uint32_t hip = rte_bswap32(pip->src_addr); + eth.s_addr.addr_bytes[2] = (hip >> 24) & 0xFF; + eth.s_addr.addr_bytes[3] = (hip >> 16) & 0xFF; + eth.s_addr.addr_bytes[4] = (hip >> 8) & 0xFF; + eth.s_addr.addr_bytes[5] = (hip) & 0xFF; + rte_memcpy(peth, ð, sizeof(struct ether_hdr)); + } + ether_addr_copy(&peth->s_addr, &key.clt_mac); +#endif + + data.tsc = rte_rdtsc() + task->cpe_timeout; + + int32_t hash_index = rte_hash_add_key(task->cpe_gre_hash, &key); + if (unlikely(hash_index < 0)) { + plog_warn("Failed to add key, gre %x\n", data.gre_id); + } + else if (unlikely(hash_index >= MAX_GRE)) { + plog_warn("Failed to add: Invalid hash_index = 0x%x\n", + hash_index); + return OUT_DISCARD; + } + rte_memcpy(&task->cpe_gre_data[hash_index], &data, sizeof(data)); + if (task->runtime_flags & TASK_TX_CRC) { + prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + } + + return 0; +} + +void handle_gre_encap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_gre_decap *task = (struct task_gre_decap *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t done = 0; + + while (n_pkts) { + uint16_t chopped = RTE_MIN(n_pkts, 16); + prefetch_pkts(mbufs, chopped); + handle_gre_encap16(task, mbufs, chopped, out + done); + mbufs += chopped; + n_pkts -= chopped; + done += chopped; + } + + task->base.tx_pkt(&task->base, mbufs - done, done, out); +} + +#define DO_ENC_ETH_OVER_GRE 1 +#define DO_ENC_IP_OVER_GRE 0 + +static inline void handle_gre_encap16(struct task_gre_decap *task, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + for (uint8_t i = 0; i < n_pkts; ++i) { + struct ether_hdr *peth = rte_pktmbuf_mtod(mbufs[i], struct ether_hdr *); + ether_addr_copy(&peth->d_addr, &task->key[i].clt_mac); + } + + int32_t hash_index[16]; + rte_hash_lookup_bulk(task->cpe_gre_hash, task->key_ptr, n_pkts, hash_index); + for (uint8_t i = 0; i < n_pkts; ++i ) { + if (unlikely(hash_index[i] < 0)) { + plog_warn("Invalid hash_index (<0) = 0x%x\n", hash_index[i]); + out[i] = OUT_DISCARD; + } + else if (unlikely(hash_index[i] >= MAX_GRE)) { + plog_warn("Invalid hash_index = 0x%x\n", hash_index[i]); + out[i] = OUT_DISCARD; + } + rte_prefetch0(&task->cpe_gre_data[hash_index[i]]); + } + + for (uint8_t i = 0; i < n_pkts; ++i ) { + if (likely(out[i] != OUT_DISCARD)) { + out[i] = handle_gre_encap(task, mbufs[i], &task->cpe_gre_data[hash_index[i]]); + } + } +} + +#ifdef DO_ENC_ETH_OVER_GRE +#define PKT_PREPEND_LEN (sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr)) +#elif DO_ENC_IP_OVER_GRE +#define PKT_PREPEND_LEN (sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr)) +#else + +static inline uint8_t handle_gre_encap(struct task_gre_decap *task, struct rte_mbuf *mbuf, struct cpe_gre_data *table) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct ipv4_hdr *pip = (struct ipv4_hdr *)(peth + 1); + uint16_t ip_len = rte_be_to_cpu_16(pip->total_length); + + struct cpe_gre_key key; + ether_addr_copy(&peth->d_addr, &key.clt_mac); + +#ifdef GRE_TP + /* policing enabled */ + if (task->cycles_per_byte) { + const uint16_t pkt_size = rte_pktmbuf_pkt_len(mbuf) + ETHER_CRC_LEN; + uint64_t tsc_now = rte_rdtsc(); + if (table->tp_tbsize < pkt_size) { + uint64_t cycles_diff = tsc_now - table->tp_tsc; + double dB = ((double)cycles_diff) / task->cycles_per_byte; + if (dB > (double)task->tb_size) { + dB = task->tb_size; + } + if ((table->tp_tbsize + dB) >= pkt_size) { + table->tp_tbsize += dB; + table->tp_tsc = tsc_now; + } + else { + TASK_STATS_ADD_DROP_DISCARD(&task->base.aux->stats, 1); + return OUT_DISCARD; + } + } + table->tp_tbsize -= pkt_size; + } +#endif /* GRE_TP */ + + /* reuse ethernet header from payload, retain payload (ip) in + case of DO_ENC_IP_OVER_GRE */ + peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, PKT_PREPEND_LEN); + PREFETCH0(peth); + ip_len += PKT_PREPEND_LEN; + + pip = (struct ipv4_hdr *)(peth + 1); + struct gre_hdr *pgre = (struct gre_hdr *)(pip + 1); + + struct ether_hdr eth = { + .d_addr = {.addr_bytes = {0x0A, 0x0A, 0x0A, 0xC8, 0x00, 0x02}}, + .s_addr = {.addr_bytes = {0x0A, 0x0A, 0x0A, 0xC8, 0x00, 0x01}}, + .ether_type = ETYPE_IPv4 + }; + rte_memcpy(peth, ð, sizeof(struct ether_hdr)); + + rte_memcpy(pgre, &gre_hdr_proto, sizeof(struct gre_hdr)); +#if DO_ENC_ETH_OVER_GRE + pgre->type = ETYPE_EoGRE; +#elif DO_ENC_IP_OVER_GRE + pgre->type = ETYPE_IPv4; +#endif + pgre->gre_id = table->gre_id; + + rte_memcpy(pip, &tunnel_ip_proto, sizeof(struct ipv4_hdr)); + pip->src_addr = 0x02010a0a; //emulate port ip + pip->dst_addr = table->cpe_ip; + pip->total_length = rte_cpu_to_be_16(ip_len); + + if (task->runtime_flags & TASK_TX_CRC) { + prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + } + + return 0; +} + +void update_arp_entries_gre(void *data) +{ + uint64_t cur_tsc = rte_rdtsc(); + struct task_gre_decap *task = (struct task_gre_decap *)data; + +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + // rte_hash_iterate might take a long time if no entries found => we should not use it here + // struct rte_hash is now internal..... + // => Not implemented +#else + uint32_t *sig_bucket = (hash_sig_t *)&(task->cpe_gre_hash->sig_tbl[task->bucket_index * task->cpe_gre_hash->sig_tbl_bucket_size]); + uint32_t table_index = task->bucket_index * task->cpe_gre_hash->bucket_entries; + + uint8_t *entry_bucket = + (uint8_t *) & task->cpe_gre_hash->key_tbl[task->bucket_index * task->cpe_gre_hash->bucket_entries * task->cpe_gre_hash->key_tbl_key_size]; + + for (uint32_t pos = 0; pos < task->cpe_gre_hash->bucket_entries; ++pos, ++table_index) { + struct cpe_gre_entry *key = (struct cpe_gre_entry *)&entry_bucket[pos * task->cpe_gre_hash->key_tbl_key_size]; + if (task->cpe_gre_data[table_index].tsc < cur_tsc) { + sig_bucket[pos] = 0; + task->cpe_gre_data[table_index].tsc = UINT64_MAX; + } + } + ++task->bucket_index; + task->bucket_index &= task->cpe_gre_hash->bucket_bitmask; +#endif +} diff --git a/VNFs/DPPD-PROX/handle_impair.c b/VNFs/DPPD-PROX/handle_impair.c new file mode 100644 index 00000000..3f2ee0eb --- /dev/null +++ b/VNFs/DPPD-PROX/handle_impair.c @@ -0,0 +1,421 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "lconf.h" +#include "log.h" +#include "random.h" +#include "handle_impair.h" +#include "prefetch.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +#define DELAY_ACCURACY 11 // accuracy of 2048 cycles ~= 1 micro-second +#define DELAY_MAX_MASK 0x1FFFFF // Maximum 2M * 2K cycles ~1 second + +struct queue_elem { + struct rte_mbuf *mbuf; + uint64_t tsc; +}; + +struct queue { + struct queue_elem *queue_elem; + unsigned queue_head; + unsigned queue_tail; +}; + +struct task_impair { + struct task_base base; + struct queue_elem *queue; + uint32_t random_delay_us; + uint32_t delay_us; + uint64_t delay_time; + uint64_t delay_time_mask; + unsigned queue_head; + unsigned queue_tail; + unsigned queue_mask; + int tresh; + unsigned int seed; + struct random state; + uint64_t last_idx; + struct queue *buffer; + uint32_t socket_id; + int need_update; +}; + +static int handle_bulk_impair(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +static int handle_bulk_impair_random(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +static int handle_bulk_random_drop(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + +void task_impair_set_proba(struct task_base *tbase, float proba) +{ + struct task_impair *task = (struct task_impair *)tbase; + task->tresh = ((uint64_t) RAND_MAX) * (uint32_t)(proba * 10000) / 1000000; +} + +void task_impair_set_delay_us(struct task_base *tbase, uint32_t delay_us, uint32_t random_delay_us) +{ + struct task_impair *task = (struct task_impair *)tbase; + task->need_update = 1; + task->random_delay_us = random_delay_us; + task->delay_us = delay_us; +} + +static void task_impair_update(struct task_base *tbase) +{ + struct task_impair *task = (struct task_impair *)tbase; + uint32_t queue_len = 0; + size_t mem_size; + if (!task->need_update) + return; + task->need_update = 0; + uint64_t now = rte_rdtsc(); + uint8_t out[MAX_PKT_BURST] = {0}; + uint64_t now_idx = (now >> DELAY_ACCURACY) & DELAY_MAX_MASK; + + if (task->random_delay_us) { + tbase->handle_bulk = handle_bulk_impair_random; + task->delay_time = usec_to_tsc(task->random_delay_us); + task->delay_time_mask = rte_align32pow2(task->delay_time) - 1; + queue_len = rte_align32pow2((1250L * task->random_delay_us) / 84 / (DELAY_MAX_MASK + 1)); + } else if (task->delay_us == 0) { + tbase->handle_bulk = handle_bulk_random_drop; + task->delay_time = 0; + } else { + tbase->handle_bulk = handle_bulk_impair; + task->delay_time = usec_to_tsc(task->delay_us); + queue_len = rte_align32pow2(1250 * task->delay_us / 84); + } + if (task->queue) { + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + while (task->queue_tail != task->queue_head) { + now = rte_rdtsc(); + uint16_t idx = 0; + while (idx < MAX_PKT_BURST && task->queue_tail != task->queue_head) { + if (task->queue[task->queue_tail].tsc <= now) { + out[idx] = rand_r(&task->seed) <= task->tresh? 0 : OUT_DISCARD; + new_mbufs[idx++] = task->queue[task->queue_tail].mbuf; + task->queue_tail = (task->queue_tail + 1) & task->queue_mask; + } + else { + break; + } + } + if (idx) + task->base.tx_pkt(&task->base, new_mbufs, idx, out); + } + prox_free(task->queue); + task->queue = NULL; + } + if (task->buffer) { + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + while (task->last_idx != ((now_idx - 1) & DELAY_MAX_MASK)) { + now = rte_rdtsc(); + uint16_t pkt_idx = 0; + while ((pkt_idx < MAX_PKT_BURST) && (task->last_idx != ((now_idx - 1) & DELAY_MAX_MASK))) { + struct queue *queue = &task->buffer[task->last_idx]; + while ((pkt_idx < MAX_PKT_BURST) && (queue->queue_tail != queue->queue_head)) { + out[pkt_idx] = rand_r(&task->seed) <= task->tresh? 0 : OUT_DISCARD; + new_mbufs[pkt_idx++] = queue->queue_elem[queue->queue_tail].mbuf; + queue->queue_tail = (queue->queue_tail + 1) & task->queue_mask; + } + task->last_idx = (task->last_idx + 1) & DELAY_MAX_MASK; + } + + if (pkt_idx) + task->base.tx_pkt(&task->base, new_mbufs, pkt_idx, out); + } + for (int i = 0; i < DELAY_MAX_MASK + 1; i++) { + if (task->buffer[i].queue_elem) + prox_free(task->buffer[i].queue_elem); + } + prox_free(task->buffer); + task->buffer = NULL; + } + + if (queue_len < MAX_PKT_BURST) + queue_len= MAX_PKT_BURST; + task->queue_mask = queue_len - 1; + if (task->queue_mask < MAX_PKT_BURST - 1) + task->queue_mask = MAX_PKT_BURST - 1; + mem_size = (task->queue_mask + 1) * sizeof(task->queue[0]); + + if (task->delay_us) { + task->queue_head = 0; + task->queue_tail = 0; + task->queue = prox_zmalloc(mem_size, task->socket_id); + if (task->queue == NULL) { + plog_err("Not enough memory to allocate queue\n"); + task->queue_mask = 0; + } + } else if (task->random_delay_us) { + size_t size = (DELAY_MAX_MASK + 1) * sizeof(struct queue); + plog_info("Allocating %zd bytes\n", size); + task->buffer = prox_zmalloc(size, task->socket_id); + PROX_PANIC(task->buffer == NULL, "Not enough memory to allocate buffer\n"); + plog_info("Allocating %d x %zd bytes\n", DELAY_MAX_MASK + 1, mem_size); + + for (int i = 0; i < DELAY_MAX_MASK + 1; i++) { + task->buffer[i].queue_elem = prox_zmalloc(mem_size, task->socket_id); + PROX_PANIC(task->buffer[i].queue_elem == NULL, "Not enough memory to allocate buffer elems\n"); + } + } + random_init_seed(&task->state); +} + +static int handle_bulk_random_drop(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_impair *task = (struct task_impair *)tbase; + uint8_t out[MAX_PKT_BURST]; + for (uint16_t i = 0; i < n_pkts; ++i) { + out[i] = rand_r(&task->seed) <= task->tresh? 0 : OUT_DISCARD; + } + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + task_impair_update(tbase); +} + +static int handle_bulk_impair(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_impair *task = (struct task_impair *)tbase; + uint64_t now = rte_rdtsc(); + uint8_t out[MAX_PKT_BURST] = {0}; + uint16_t enqueue_failed; + uint16_t i; + int ret = 0; + + int nb_empty_slots = (task->queue_tail - task->queue_head + task->queue_mask) & task->queue_mask; + if (likely(nb_empty_slots >= n_pkts)) { + /* We know n_pkts fits, no need to check for every packet */ + for (i = 0; i < n_pkts; ++i) { + task->queue[task->queue_head].tsc = now + task->delay_time; + task->queue[task->queue_head].mbuf = mbufs[i]; + task->queue_head = (task->queue_head + 1) & task->queue_mask; + } + } else { + for (i = 0; i < n_pkts; ++i) { + if (((task->queue_head + 1) & task->queue_mask) != task->queue_tail) { + task->queue[task->queue_head].tsc = now + task->delay_time; + task->queue[task->queue_head].mbuf = mbufs[i]; + task->queue_head = (task->queue_head + 1) & task->queue_mask; + } + else { + /* Rest does not fit, need to drop those packets. */ + enqueue_failed = i; + for (;i < n_pkts; ++i) { + out[i] = OUT_DISCARD; + } + ret+= task->base.tx_pkt(&task->base, mbufs + enqueue_failed, + n_pkts - enqueue_failed, out + enqueue_failed); + break; + } + } + } + + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + uint16_t idx = 0; + + if (task->tresh != RAND_MAX) { + while (idx < MAX_PKT_BURST && task->queue_tail != task->queue_head) { + if (task->queue[task->queue_tail].tsc <= now) { + out[idx] = rand_r(&task->seed) <= task->tresh? 0 : OUT_DISCARD; + new_mbufs[idx] = task->queue[task->queue_tail].mbuf; + PREFETCH0(new_mbufs[idx]); + PREFETCH0(&new_mbufs[idx]->cacheline1); + idx++; + task->queue_tail = (task->queue_tail + 1) & task->queue_mask; + } + else { + break; + } + } + } else { + while (idx < MAX_PKT_BURST && task->queue_tail != task->queue_head) { + if (task->queue[task->queue_tail].tsc <= now) { + out[idx] = 0; + new_mbufs[idx] = task->queue[task->queue_tail].mbuf; + PREFETCH0(new_mbufs[idx]); + PREFETCH0(&new_mbufs[idx]->cacheline1); + idx++; + task->queue_tail = (task->queue_tail + 1) & task->queue_mask; + } + else { + break; + } + } + } + + if (idx) + ret+= task->base.tx_pkt(&task->base, new_mbufs, idx, out); + task_impair_update(tbase); + return ret; +} + +/* + * We want to avoid using division and mod for performance reasons. + * We also want to support up to one second delay, and express it in tsc + * So the delay in tsc needs up to 32 bits (supposing procesor freq is less than 4GHz). + * If the max_delay is smaller, we make sure we use less bits. + * Note that we lose the MSB of the xorshift - 64 bits could hold + * two or three delays in TSC - but would probably make implementation more complex + * and not huge gain expected. Maybe room for optimization. + * Using this implementation, we might have to run random more than once for a delay + * but in average this should occur less than 50% of the time. +*/ + +static inline uint64_t random_delay(struct random *state, uint64_t max_delay, uint64_t max_delay_mask) +{ + uint64_t val; + while(1) { + val = random_next(state); + if ((val & max_delay_mask) < max_delay) + return (val & max_delay_mask); + } +} + +static int handle_bulk_impair_random(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_impair *task = (struct task_impair *)tbase; + uint64_t now = rte_rdtsc(); + uint8_t out[MAX_PKT_BURST]; + uint16_t enqueue_failed; + uint16_t i; + int ret = 0; + uint64_t packet_time, idx; + uint64_t now_idx = (now >> DELAY_ACCURACY) & DELAY_MAX_MASK; + + for (i = 0; i < n_pkts; ++i) { + packet_time = now + random_delay(&task->state, task->delay_time, task->delay_time_mask); + idx = (packet_time >> DELAY_ACCURACY) & DELAY_MAX_MASK; + while (idx != ((now_idx - 1) & DELAY_MAX_MASK)) { + struct queue *queue = &task->buffer[idx]; + if (((queue->queue_head + 1) & task->queue_mask) != queue->queue_tail) { + queue->queue_elem[queue->queue_head].mbuf = mbufs[i]; + queue->queue_head = (queue->queue_head + 1) & task->queue_mask; + break; + } else { + idx = (idx + 1) & DELAY_MAX_MASK; + } + } + if (idx == ((now_idx - 1) & DELAY_MAX_MASK)) { + /* Rest does not fit, need to drop packet. Note that further packets might fit as might want to be sent earlier */ + out[0] = OUT_DISCARD; + ret+= task->base.tx_pkt(&task->base, mbufs + i, 1, out); + plog_warn("Unexpectdly dropping packets\n"); + } + } + + struct rte_mbuf *new_mbufs[MAX_PKT_BURST]; + uint16_t pkt_idx = 0; + + while ((pkt_idx < MAX_PKT_BURST) && (task->last_idx != ((now_idx - 1) & DELAY_MAX_MASK))) { + struct queue *queue = &task->buffer[task->last_idx]; + while ((pkt_idx < MAX_PKT_BURST) && (queue->queue_tail != queue->queue_head)) { + out[pkt_idx] = rand_r(&task->seed) <= task->tresh? 0 : OUT_DISCARD; + new_mbufs[pkt_idx] = queue->queue_elem[queue->queue_tail].mbuf; + PREFETCH0(new_mbufs[pkt_idx]); + PREFETCH0(&new_mbufs[pkt_idx]->cacheline1); + pkt_idx++; + queue->queue_tail = (queue->queue_tail + 1) & task->queue_mask; + } + task->last_idx = (task->last_idx + 1) & DELAY_MAX_MASK; + } + + if (pkt_idx) + ret+= task->base.tx_pkt(&task->base, new_mbufs, pkt_idx, out); + task_impair_update(tbase); + return ret; +} + +static void init_task(struct task_base *tbase, struct task_args *targ) +{ + struct task_impair *task = (struct task_impair *)tbase; + uint32_t queue_len = 0; + size_t mem_size; + unsigned socket_id; + uint64_t delay_us = 0; + + task->seed = rte_rdtsc(); + if (targ->probability == 0) + targ->probability = 1000000; + + task->tresh = ((uint64_t) RAND_MAX) * targ->probability / 1000000; + + if ((targ->delay_us == 0) && (targ->random_delay_us == 0)) { + tbase->handle_bulk = handle_bulk_random_drop; + task->delay_time = 0; + } else if (targ->random_delay_us) { + tbase->handle_bulk = handle_bulk_impair_random; + task->delay_time = usec_to_tsc(targ->random_delay_us); + task->delay_time_mask = rte_align32pow2(task->delay_time) - 1; + delay_us = targ->random_delay_us; + queue_len = rte_align32pow2((1250L * delay_us) / 84 / (DELAY_MAX_MASK + 1)); + } else { + task->delay_time = usec_to_tsc(targ->delay_us); + delay_us = targ->delay_us; + queue_len = rte_align32pow2(1250 * delay_us / 84); + } + /* Assume Line-rate is maximum transmit speed. + TODO: take link speed if tx is port. + */ + if (queue_len < MAX_PKT_BURST) + queue_len= MAX_PKT_BURST; + task->queue_mask = queue_len - 1; + if (task->queue_mask < MAX_PKT_BURST - 1) + task->queue_mask = MAX_PKT_BURST - 1; + + mem_size = (task->queue_mask + 1) * sizeof(task->queue[0]); + socket_id = rte_lcore_to_socket_id(targ->lconf->id); + task->socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + if (targ->delay_us) { + task->queue = prox_zmalloc(mem_size, socket_id); + PROX_PANIC(task->queue == NULL, "Not enough memory to allocate queue\n"); + task->queue_head = 0; + task->queue_tail = 0; + } else if (targ->random_delay_us) { + size_t size = (DELAY_MAX_MASK + 1) * sizeof(struct queue); + plog_info("Allocating %zd bytes\n", size); + task->buffer = prox_zmalloc(size, socket_id); + PROX_PANIC(task->buffer == NULL, "Not enough memory to allocate buffer\n"); + plog_info("Allocating %d x %zd bytes\n", DELAY_MAX_MASK + 1, mem_size); + + for (int i = 0; i < DELAY_MAX_MASK + 1; i++) { + task->buffer[i].queue_elem = prox_zmalloc(mem_size, socket_id); + PROX_PANIC(task->buffer[i].queue_elem == NULL, "Not enough memory to allocate buffer elems\n"); + } + } + random_init_seed(&task->state); +} + +static struct task_init tinit = { + .mode_str = "impair", + .init = init_task, + .handle = handle_bulk_impair, + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_ZERO_RX, + .size = sizeof(struct task_impair) +}; + +__attribute__((constructor)) static void ctor(void) +{ + reg_task(&tinit); +} diff --git a/VNFs/DPPD-PROX/handle_impair.h b/VNFs/DPPD-PROX/handle_impair.h new file mode 100644 index 00000000..162213ed --- /dev/null +++ b/VNFs/DPPD-PROX/handle_impair.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_IMPAIR_H_ +#define _HANDLE_IMPAIR_H_ + +void task_impair_set_delay_us(struct task_base *tbase, uint32_t delay_us, uint32_t random_delay_us); +void task_impair_set_proba(struct task_base *tbase, float proba); + +#endif /* _HANDLE_IMPAIR_H_ */ diff --git a/VNFs/DPPD-PROX/handle_ipv6_tunnel.c b/VNFs/DPPD-PROX/handle_ipv6_tunnel.c new file mode 100644 index 00000000..a92f9cdc --- /dev/null +++ b/VNFs/DPPD-PROX/handle_ipv6_tunnel.c @@ -0,0 +1,466 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_tcp.h> +#include <rte_table_hash.h> +#include <rte_ether.h> +#include <rte_version.h> +#include <rte_byteorder.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "tx_pkt.h" +#include "task_init.h" +#include "task_base.h" +#include "prox_port_cfg.h" +#include "prefetch.h" +#include "lconf.h" +#include "hash_utils.h" +#include "etypes.h" +#include "prox_cksum.h" +#include "defines.h" +#include "log.h" +#include "quit.h" +#include "prox_cfg.h" +#include "parse_utils.h" +#include "cfgfile.h" +#include "prox_shared.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define IPPROTO_IPIP IPPROTO_IPV4 +#endif + +struct ipv6_tun_dest { + struct ipv6_addr dst_addr; + struct ether_addr dst_mac; +}; + +typedef enum ipv6_tun_dir_t { + TUNNEL_DIR_ENCAP = 0, + TUNNEL_DIR_DECAP = 1, +} ipv6_tun_dir_t; + +struct task_ipv6_tun_base { + struct task_base base; + struct ether_addr src_mac; + uint8_t core_nb; + uint64_t keys[64]; + struct rte_mbuf* fake_packets[64]; + uint16_t lookup_port_mask; // Mask used before looking up the port + void* lookup_table; // Fast lookup table for bindings + uint32_t runtime_flags; + int offload_crc; +}; + +struct task_ipv6_decap { + struct task_ipv6_tun_base base; + struct ether_addr dst_mac; +}; + +struct task_ipv6_encap { + struct task_ipv6_tun_base base; + uint32_t ipaddr; + struct ipv6_addr local_endpoint_addr; + uint8_t tunnel_hop_limit; +}; + +#define IPv6_VERSION 6 +#ifndef IPPROTO_IPV4 +#define IPPROTO_IPV4 4 +#endif + +#define MAKE_KEY_FROM_FIELDS(ipv4_addr, port, port_mask) ( ((uint64_t)ipv4_addr << 16) | (port & port_mask) ) + +static int handle_ipv6_decap_bulk(struct task_base* tbase, struct rte_mbuf** rx_mbuf, const uint16_t n_pkts); +static int handle_ipv6_encap_bulk(struct task_base* tbase, struct rte_mbuf** rx_mbuf, const uint16_t n_pkts); + +static void init_lookup_table(struct task_ipv6_tun_base* ptask, struct task_args *targ) +{ + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + /* The lookup table is a per-core data structure to reduce the + memory footprint and improve cache utilization. Since + operations on the hash table are not safe, the data + structure can't be used on a per socket or on a system wide + basis. */ + ptask->lookup_table = prox_sh_find_core(targ->lconf->id, "ipv6_binding_table"); + if (NULL == ptask->lookup_table) { + struct ipv6_tun_binding_table *table; + PROX_PANIC(!strcmp(targ->tun_bindings, ""), "No tun bindings specified\n"); + int ret = lua_to_ip6_tun_binding(prox_lua(), GLOBAL, targ->tun_bindings, socket_id, &table); + PROX_PANIC(ret, "Failed to read tun_bindings config:\n %s\n", get_lua_to_errors()); + + struct rte_table_hash_key8_ext_params table_hash_params = { + .n_entries = (table->num_binding_entries * 4), + .n_entries_ext = (table->num_binding_entries * 2) >> 1, + .f_hash = hash_crc32, + .seed = 0, + .signature_offset = HASH_METADATA_OFFSET(8), // Ignored for dosig tables + .key_offset = HASH_METADATA_OFFSET(0), + }; + plogx_info("IPv6 Tunnel allocating lookup table on socket %d\n", socket_id); + ptask->lookup_table = rte_table_hash_key8_ext_dosig_ops. + f_create(&table_hash_params, socket_id, sizeof(struct ipv6_tun_dest)); + PROX_PANIC(ptask->lookup_table == NULL, "Error creating IPv6 Tunnel lookup table"); + + for (unsigned idx = 0; idx < table->num_binding_entries; idx++) { + int key_found = 0; + void* entry_in_hash = NULL; + struct ipv6_tun_dest data; + struct ipv6_tun_binding_entry* entry = &table->entry[idx]; + uint64_t key = MAKE_KEY_FROM_FIELDS(rte_cpu_to_be_32(entry->public_ipv4), entry->public_port, ptask->lookup_port_mask); + rte_memcpy(&data.dst_addr, &entry->endpoint_addr, sizeof(struct ipv6_addr)); + rte_memcpy(&data.dst_mac, &entry->next_hop_mac, sizeof(struct ether_addr)); + + int ret = rte_table_hash_key8_ext_dosig_ops.f_add(ptask->lookup_table, &key, &data, &key_found, &entry_in_hash); + PROX_PANIC(ret, "Error adding entry (%d) to binding lookup table", idx); + PROX_PANIC(key_found, "key_found!!! for idx=%d\n", idx); + +#ifdef DBG_IPV6_TUN_BINDING + plog_info("Bind: %x:0x%x (port_mask 0x%x) key=0x%"PRIx64"\n", entry->public_ipv4, entry->public_port, ptask->lookup_port_mask, key); + plog_info(" -> "IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(entry->endpoint_addr.bytes), MAC_BYTES(entry->next_hop_mac.addr_bytes)); + plog_info(" -> "IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(data.dst_addr.bytes), MAC_BYTES(data.dst_mac.addr_bytes)); + plog_info(" -> entry_in_hash=%p\n", entry_in_hash); +#endif + } + plogx_info("IPv6 Tunnel created %d lookup table entries\n", table->num_binding_entries); + + prox_sh_add_core(targ->lconf->id, "ipv6_binding_table", ptask->lookup_table); + } +} + +static void init_task_ipv6_tun_base(struct task_ipv6_tun_base* tun_base, struct task_args* targ) +{ + memcpy(&tun_base->src_mac, find_reachable_port(targ), sizeof(tun_base->src_mac)); + + tun_base->lookup_port_mask = targ->lookup_port_mask; // Mask used before looking up the port + + init_lookup_table(tun_base, targ); + + for (uint32_t i = 0; i < 64; ++i) { + tun_base->fake_packets[i] = (struct rte_mbuf*)((uint8_t*)&tun_base->keys[i] - sizeof (struct rte_mbuf)); + } + + plogx_info("IPv6 Tunnel MAC="MAC_BYTES_FMT" port_mask=0x%x\n", + MAC_BYTES(tun_base->src_mac.addr_bytes), tun_base->lookup_port_mask); + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + tun_base->offload_crc = port->capabilities.tx_offload_cksum; + } +} + +static void init_task_ipv6_decap(struct task_base* tbase, struct task_args* targ) +{ + struct task_ipv6_decap* tun_task = (struct task_ipv6_decap*)tbase; + struct task_ipv6_tun_base* tun_base = (struct task_ipv6_tun_base*)tun_task; + + init_task_ipv6_tun_base(tun_base, targ); + tun_base->runtime_flags = targ->runtime_flags; + + memcpy(&tun_task->dst_mac, &targ->edaddr, sizeof(tun_task->dst_mac)); +} + +static void init_task_ipv6_encap(struct task_base* tbase, struct task_args* targ) +{ + struct task_ipv6_encap* tun_task = (struct task_ipv6_encap*)tbase; + struct task_ipv6_tun_base *tun_base = (struct task_ipv6_tun_base*)tun_task; + + init_task_ipv6_tun_base(tun_base, targ); + + rte_memcpy(&tun_task->local_endpoint_addr, &targ->local_ipv6, sizeof(tun_task->local_endpoint_addr)); + tun_task->tunnel_hop_limit = targ->tunnel_hop_limit; + tun_base->runtime_flags = targ->runtime_flags; +} + +static struct task_init task_init_ipv6_decap = { + .mode_str = "ipv6_decap", + .init = init_task_ipv6_decap, + .handle = handle_ipv6_decap_bulk, + .size = sizeof(struct task_ipv6_decap) +}; + +static struct task_init task_init_ipv6_encap = { + .mode_str = "ipv6_encap", + .init = init_task_ipv6_encap, + .handle = handle_ipv6_encap_bulk, + .size = sizeof(struct task_ipv6_encap) +}; + +__attribute__((constructor)) static void reg_task_ipv6_decap(void) +{ + reg_task(&task_init_ipv6_decap); +} + +__attribute__((constructor)) static void reg_task_ipv6_encap(void) +{ + reg_task(&task_init_ipv6_encap); +} + +static inline uint8_t handle_ipv6_decap(struct task_ipv6_decap* ptask, struct rte_mbuf* rx_mbuf, struct ipv6_tun_dest* tun_dest); +static inline uint8_t handle_ipv6_encap(struct task_ipv6_encap* ptask, struct rte_mbuf* rx_mbuf, struct ipv6_tun_dest* tun_dest); + +static inline int extract_key_fields( __attribute__((unused)) struct task_ipv6_tun_base* ptask, struct ipv4_hdr* pip4, ipv6_tun_dir_t dir, uint32_t* pAddr, uint16_t* pPort) +{ + *pAddr = (dir == TUNNEL_DIR_DECAP) ? pip4->src_addr : pip4->dst_addr; + + if (pip4->next_proto_id == IPPROTO_UDP) { + struct udp_hdr* pudp = (struct udp_hdr *)(pip4 + 1); + *pPort = rte_be_to_cpu_16((dir == TUNNEL_DIR_DECAP) ? pudp->src_port : pudp->dst_port); + } + else if (pip4->next_proto_id == IPPROTO_TCP) { + struct tcp_hdr* ptcp = (struct tcp_hdr *)(pip4 + 1); + *pPort = rte_be_to_cpu_16((dir == TUNNEL_DIR_DECAP) ? ptcp->src_port : ptcp->dst_port); + } + else { + plog_warn("IPv6 Tunnel: IPv4 packet of unexpected type proto_id=0x%x\n", pip4->next_proto_id); + *pPort = 0xffff; + return -1; + } + + return 0; +} + +static inline void extract_key(struct task_ipv6_tun_base* ptask, struct ipv4_hdr* pip4, ipv6_tun_dir_t dir, uint64_t* pkey) +{ + uint32_t lookup_addr; + uint16_t lookup_port; + + if (unlikely( extract_key_fields(ptask, pip4, dir, &lookup_addr, &lookup_port))) { + plog_warn("IPv6 Tunnel: Unable to extract fields from packet\n"); + *pkey = 0xffffffffL; + return; + } + + *pkey = MAKE_KEY_FROM_FIELDS(lookup_addr, lookup_port, ptask->lookup_port_mask); +} + +static inline struct ipv4_hdr* get_ipv4_decap(struct rte_mbuf *mbuf) +{ + struct ether_hdr* peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1); + struct ipv4_hdr* pip4 = (struct ipv4_hdr*) (pip6 + 1); // TODO - Skip Option headers + + return pip4; +} + +static inline struct ipv4_hdr* get_ipv4_encap(struct rte_mbuf *mbuf) +{ + struct ether_hdr* peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct ipv4_hdr* pip4 = (struct ipv4_hdr *)(peth + 1); + + return pip4; +} + +static inline void extract_key_decap(struct task_ipv6_tun_base* ptask, struct rte_mbuf *mbuf, uint64_t* pkey) +{ + extract_key(ptask, get_ipv4_decap(mbuf), TUNNEL_DIR_DECAP, pkey); +} + +static inline void extract_key_decap_bulk(struct task_ipv6_tun_base* ptask, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + extract_key_decap(ptask, mbufs[j], &ptask->keys[j]); + } +} + +static inline void extract_key_encap(struct task_ipv6_tun_base* ptask, struct rte_mbuf *mbuf, uint64_t* pkey) +{ + extract_key(ptask, get_ipv4_encap(mbuf), TUNNEL_DIR_ENCAP, pkey); +} + +static inline void extract_key_encap_bulk(struct task_ipv6_tun_base* ptask, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + extract_key_encap(ptask, mbufs[j], &ptask->keys[j]); + } +} + +__attribute__((cold)) static void handle_error(struct task_ipv6_tun_base* ptask, struct rte_mbuf* mbuf, ipv6_tun_dir_t dir) +{ + uint32_t lookup_addr; + uint16_t lookup_port; + uint64_t key; + + struct ipv4_hdr* pip4 = (dir == TUNNEL_DIR_DECAP) ? get_ipv4_decap(mbuf) : get_ipv4_encap(mbuf); + extract_key_fields(ptask, pip4, dir, &lookup_addr, &lookup_port); + extract_key(ptask, pip4, dir, &key); + + plog_warn("IPv6 Tunnel (%s) lookup failed for "IPv4_BYTES_FMT":%d [key=0x%"PRIx64"]\n", + (dir == TUNNEL_DIR_DECAP) ? "decap" : "encap", + IPv4_BYTES(((unsigned char*)&lookup_addr)), lookup_port, key); +} + +static int handle_ipv6_decap_bulk(struct task_base* tbase, struct rte_mbuf** mbufs, const uint16_t n_pkts) +{ + struct task_ipv6_decap* task = (struct task_ipv6_decap *)tbase; + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + struct ipv6_tun_dest* entries[64]; + uint8_t out[MAX_PKT_BURST]; + uint64_t lookup_hit_mask; + uint16_t n_kept = 0; + + prefetch_pkts(mbufs, n_pkts); + + // Lookup to verify packets are valid for their respective tunnels (their sending lwB4) + extract_key_decap_bulk(&task->base, mbufs, n_pkts); + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->base.lookup_table, task->base.fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (likely(lookup_hit_mask == pkts_mask)) { + for (uint16_t j = 0; j < n_pkts; ++j) { + out[j] = handle_ipv6_decap(task, mbufs[j], entries[j]); + } + } + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + handle_error(&task->base, mbufs[j], TUNNEL_DIR_DECAP); + out[j] = OUT_DISCARD; + continue; + } + out[j] = handle_ipv6_decap(task, mbufs[j], entries[j]); + } + } + + return task->base.base.tx_pkt(tbase, mbufs, n_pkts, out); +} + +static int handle_ipv6_encap_bulk(struct task_base* tbase, struct rte_mbuf** mbufs, const uint16_t n_pkts) +{ + struct task_ipv6_encap* task = (struct task_ipv6_encap *)tbase; + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + struct ipv6_tun_dest* entries[64]; + uint64_t lookup_hit_mask; + uint8_t out[MAX_PKT_BURST]; + uint16_t n_kept = 0; + + prefetch_first(mbufs, n_pkts); + + extract_key_encap_bulk(&task->base, mbufs, n_pkts); + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->base.lookup_table, task->base.fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (likely(lookup_hit_mask == pkts_mask)) { + for (uint16_t j = 0; j < n_pkts; ++j) { + out[j] = handle_ipv6_encap(task, mbufs[j], entries[j]); + } + } + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + handle_error(&task->base, mbufs[j], TUNNEL_DIR_ENCAP); + out[j] = OUT_DISCARD; + continue; + } + out[j] = handle_ipv6_encap(task, mbufs[j], entries[j]); + } + } + + return task->base.base.tx_pkt(tbase, mbufs, n_pkts, out); +} + +static inline uint8_t handle_ipv6_decap(struct task_ipv6_decap* ptask, struct rte_mbuf* rx_mbuf, __attribute__((unused)) struct ipv6_tun_dest* tun_dest) +{ + struct ether_hdr* peth = rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *); + + if (unlikely(peth->ether_type != ETYPE_IPv6)) { + plog_warn("Received non IPv6 packet on ipv6 tunnel port\n"); + // Drop packet + return OUT_DISCARD; + } + + struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1); + int ipv6_hdr_len = sizeof(struct ipv6_hdr); + + // TODO - Skip over any IPv6 Extension Header: + // If pip6->next_header is in (0, 43, 44, 50, 51, 60, 135), skip ahead pip->hdr_ext_len + // bytes and repeat. Increase ipv6_hdr_len with as much, each time. + + if (unlikely(pip6->proto != IPPROTO_IPIP)) { + plog_warn("Received non IPv4 content within IPv6 tunnel packet\n"); + // Drop packet + return OUT_DISCARD; + } + + // Discard IPv6 encapsulation + rte_pktmbuf_adj(rx_mbuf, ipv6_hdr_len); + peth = rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *); + + // Restore Ethernet header + ether_addr_copy(&ptask->base.src_mac, &peth->s_addr); + ether_addr_copy(&ptask->dst_mac, &peth->d_addr); + peth->ether_type = ETYPE_IPv4; + + return 0; +} + +static inline uint8_t handle_ipv6_encap(struct task_ipv6_encap* ptask, struct rte_mbuf* rx_mbuf, __attribute__((unused)) struct ipv6_tun_dest* tun_dest) +{ + //plog_info("Found tunnel endpoint:"IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(tun_dest->dst_addr), MAC_BYTES(tun_dest->dst_mac.addr_bytes)); + + struct ether_hdr* peth = (struct ether_hdr *)(rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *)); + struct ipv4_hdr* pip4 = (struct ipv4_hdr *)(peth + 1); + uint16_t ipv4_length = rte_be_to_cpu_16(pip4->total_length); + struct task_ipv6_tun_base* tun_base = (struct task_ipv6_tun_base*)ptask; + + if (unlikely((pip4->version_ihl >> 4) != 4)) { + plog_warn("Received non IPv4 packet at ipv6 tunnel input\n"); + // Drop packet + return OUT_DISCARD; + } + + if (pip4->time_to_live) { + pip4->time_to_live--; + } + else { + plog_info("TTL = 0 => Dropping\n"); + return OUT_DISCARD; + } + pip4->hdr_checksum = 0; + + // Remove padding if any (we don't want to encapsulate garbage at end of IPv4 packet) + int padding = rte_pktmbuf_pkt_len(rx_mbuf) - (ipv4_length + sizeof(struct ether_hdr)); + if (unlikely(padding > 0)) { + rte_pktmbuf_trim(rx_mbuf, padding); + } + + // Encapsulate + const int extra_space = sizeof(struct ipv6_hdr); + peth = (struct ether_hdr *)rte_pktmbuf_prepend(rx_mbuf, extra_space); + + // Ethernet Header + ether_addr_copy(&ptask->base.src_mac, &peth->s_addr); + ether_addr_copy(&tun_dest->dst_mac, &peth->d_addr); + peth->ether_type = ETYPE_IPv6; + + // Set up IPv6 Header + struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1); + pip6->vtc_flow = rte_cpu_to_be_32(IPv6_VERSION << 28); + pip6->proto = IPPROTO_IPIP; + pip6->payload_len = rte_cpu_to_be_16(ipv4_length); + pip6->hop_limits = ptask->tunnel_hop_limit; + rte_memcpy(pip6->dst_addr, &tun_dest->dst_addr, sizeof(pip6->dst_addr)); + rte_memcpy(pip6->src_addr, &ptask->local_endpoint_addr, sizeof(pip6->src_addr)); + + if (tun_base->runtime_flags & TASK_TX_CRC) { + // We modified the TTL in the IPv4 header, hence have to recompute the IPv4 checksum +#define TUNNEL_L2_LEN (sizeof(struct ether_hdr) + sizeof(struct ipv6_hdr)) + prox_ip_cksum(rx_mbuf, pip4, TUNNEL_L2_LEN, sizeof(struct ipv4_hdr), ptask->base.offload_crc); + } + return 0; +} diff --git a/VNFs/DPPD-PROX/handle_irq.c b/VNFs/DPPD-PROX/handle_irq.c new file mode 100644 index 00000000..4abf84a1 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_irq.c @@ -0,0 +1,169 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "lconf.h" +#include "task_base.h" +#include "task_init.h" +#include "handle_irq.h" +#include "log.h" +#include "unistd.h" +#include "input.h" + +#define MAX_INDEX 65535 * 16 + +struct irq_info { + uint64_t tsc; + uint64_t lat; +}; + +struct irq_bucket { + uint64_t index; + struct irq_info info[MAX_INDEX]; +}; + +struct task_irq { + struct task_base base; + uint64_t start_tsc; + uint64_t tsc; + uint64_t max_irq; + uint8_t lcore_id; + volatile uint16_t stats_use_lt; /* which lt to use, */ + volatile uint16_t task_use_lt; /* 0 or 1 depending on which of the 2 result records are used */ + struct irq_bucket buffer[2]; +}; + +#define MAX_INTERRUPT_LENGTH 500000 /* Maximum length of an interrupt is (1 / MAX_INTERRUPT_LENGTH) seconds */ + +/* + * This module is not handling any packets. + * It loops on rdtsc() and checks whether it has been interrupted + * for more than (1 / MAX_INTERRUPT_LENGTH) sec. + * This is a debugging only task, useful to check if the system h + * as been properly configured. +*/ + +void task_irq_show_stats(struct task_irq *task_irq, struct input *input) +{ + struct irq_bucket *bucket = &task_irq->buffer[!task_irq->task_use_lt]; + if (input->reply) { + char buf[8192] = {0}; + if (bucket->index == 0) { + sprintf(buf, "\n"); + input->reply(input, buf, strlen(buf)); + buf[0] = 0; + } + for (uint64_t i = 0; i < bucket->index; i++) { + sprintf(buf + strlen(buf), "%d; %"PRIu64"""; %ld; %ld; %ld; %ld ;", + task_irq->lcore_id, + i, + bucket->info[i].lat, + bucket->info[i].lat * 1000000 / rte_get_tsc_hz(), + bucket->info[i].tsc - task_irq->start_tsc, + (bucket->info[i].tsc - task_irq->start_tsc) * 1000 / rte_get_tsc_hz()); + sprintf(buf+strlen(buf), "\n"); + input->reply(input, buf, strlen(buf)); + buf[0] = 0; + } + } else { + for (uint64_t i = 0; i < bucket->index; i++) + if (bucket->info[i].lat) + plog_info("[%d]; Interrupt %"PRIu64": %ld cycles (%ld micro-sec) at %ld cycles (%ld msec)\n", + task_irq->lcore_id, + i, + bucket->info[i].lat, + bucket->info[i].lat * 1000000 / rte_get_tsc_hz(), + bucket->info[i].tsc - task_irq->start_tsc, + (bucket->info[i].tsc - task_irq->start_tsc) * 1000 / rte_get_tsc_hz()); + } + task_irq->stats_use_lt = !task_irq->task_use_lt; + bucket->index = 0; +} + +static void irq_stop(struct task_base *tbase) +{ + struct task_irq *task = (struct task_irq *)tbase; + uint32_t i; + uint32_t lcore_id = rte_lcore_id(); + int bucket_id; + + plog_info("Stopping core %u\n", lcore_id); + plog_info("Core ID; Interrupt (nanosec); Time (msec)\n"); + for (int j = 0; j < 2; j++) { + // Start dumping the oldest bucket first + if (task->buffer[0].info[0].tsc < task->buffer[1].info[0].tsc) + bucket_id = j; + else + bucket_id = !j; + struct irq_bucket *bucket = &task->buffer[bucket_id]; + for (i=0; i< bucket->index;i++) { + if (bucket->info[i].lat != 0) { + plog_info("%d; %ld; %ld\n", + lcore_id, + bucket->info[i].lat * 1000000000 / rte_get_tsc_hz(), + (bucket->info[i].tsc - task->start_tsc) * 1000 / rte_get_tsc_hz()); + } + } + } + plog_info("Core %u stopped\n", lcore_id); +} + +static inline int handle_irq_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_irq *task = (struct task_irq *)tbase; + uint64_t tsc1; + uint64_t index; + + if (task->stats_use_lt != task->task_use_lt) + task->task_use_lt = task->stats_use_lt; + struct irq_bucket *bucket = &task->buffer[task->task_use_lt]; + + tsc1 = rte_rdtsc(); + if ((task->tsc != 0) && ((tsc1 - task->tsc) > task->max_irq) && (bucket->index < MAX_INDEX)) { + bucket->info[bucket->index].tsc = tsc1; + bucket->info[bucket->index++].lat = tsc1 - task->tsc; + } + task->tsc = tsc1; + return 0; +} + +static void init_task_irq(struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ + struct task_irq *task = (struct task_irq *)tbase; + // max_irq expressed in cycles + task->max_irq = rte_get_tsc_hz() / MAX_INTERRUPT_LENGTH; + task->start_tsc = rte_rdtsc(); + task->lcore_id = targ->lconf->id; + plog_info("\tusing irq mode with max irq set to %ld cycles\n", task->max_irq); +} + +static struct task_init task_init_irq = { + .mode_str = "irq", + .init = init_task_irq, + .handle = handle_irq_bulk, + .stop = irq_stop, + .flag_features = TASK_FEATURE_NO_RX, + .size = sizeof(struct task_irq) +}; + +static struct task_init task_init_none; + +__attribute__((constructor)) static void reg_task_irq(void) +{ + reg_task(&task_init_irq); +} diff --git a/VNFs/DPPD-PROX/handle_irq.h b/VNFs/DPPD-PROX/handle_irq.h new file mode 100644 index 00000000..784bf0d6 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_irq.h @@ -0,0 +1,25 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_IRQ_H_ +#define _HANDLE_IRQ_H_ + +struct task_irq; +struct input; + +void task_irq_show_stats(struct task_irq *task_irq, struct input *input); + +#endif /* _HANDLE_IRQ_H_ */ diff --git a/VNFs/DPPD-PROX/handle_l2fwd.c b/VNFs/DPPD-PROX/handle_l2fwd.c new file mode 100644 index 00000000..79a5f02e --- /dev/null +++ b/VNFs/DPPD-PROX/handle_l2fwd.c @@ -0,0 +1,117 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> + +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "prox_port_cfg.h" + +struct task_l2fwd { + struct task_base base; + uint8_t src_dst_mac[12]; + uint32_t runtime_flags; +}; + +static int handle_l2fwd_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_l2fwd *task = (struct task_l2fwd *)tbase; + struct ether_hdr *hdr; + struct ether_addr mac; + + if ((task->runtime_flags & (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET)) == (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET)) { + /* Source and Destination mac hardcoded */ + for (uint16_t j = 0; j < n_pkts; ++j) { + hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *); + rte_memcpy(hdr, task->src_dst_mac, sizeof(task->src_dst_mac)); + } + } else { + for (uint16_t j = 0; j < n_pkts; ++j) { + hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *); + if ((task->runtime_flags & (TASK_ARG_DO_NOT_SET_SRC_MAC|TASK_ARG_SRC_MAC_SET)) == 0) { + /* dst mac will be used as src mac */ + ether_addr_copy(&hdr->d_addr, &mac); + } + + if (task->runtime_flags & TASK_ARG_DST_MAC_SET) + ether_addr_copy((struct ether_addr *)&task->src_dst_mac[0], &hdr->d_addr); + else if ((task->runtime_flags & TASK_ARG_DO_NOT_SET_DST_MAC) == 0) + ether_addr_copy(&hdr->s_addr, &hdr->d_addr); + + if (task->runtime_flags & TASK_ARG_SRC_MAC_SET) { + ether_addr_copy((struct ether_addr *)&task->src_dst_mac[6], &hdr->s_addr); + } else if ((task->runtime_flags & TASK_ARG_DO_NOT_SET_SRC_MAC) == 0) { + ether_addr_copy(&mac, &hdr->s_addr); + } + } + } + return task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); +} + +static void init_task_l2fwd(struct task_base *tbase, struct task_args *targ) +{ + struct task_l2fwd *task = (struct task_l2fwd *)tbase; + struct ether_addr *src_addr, *dst_addr; + + /* + * Destination MAC can come from + * - pre-configured mac in case 'dst mac=xx:xx:xx:xx:xx:xx' in config file + * - src mac from the packet in case 'dst mac=packet' in config file + * - not written in case 'dst mac=no' in config file + * - (default - no 'dst mac') src mac from the packet + * Source MAC can come from + * - pre-configured mac in case 'src mac=xx:xx:xx:xx:xx:xx' in config file + * - dst mac from the packet in case 'src mac=packet' in config file + * - not written in case 'src mac=no' in config file + * - (default - no 'src mac') if (tx_port) port mac + * - (default - no 'src mac') if (no tx_port) dst mac from the packet + */ + + if (targ->flags & TASK_ARG_DST_MAC_SET) { + dst_addr = &targ->edaddr; + memcpy(&task->src_dst_mac[0], dst_addr, sizeof(*src_addr)); + } + + if (targ->flags & TASK_ARG_SRC_MAC_SET) { + src_addr = &targ->esaddr; + memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr)); + plog_info("\t\tCore %d: src mac set from config file\n", targ->lconf->id); + } else if ((targ->flags & TASK_ARG_DO_NOT_SET_SRC_MAC) == 0) { + if (targ->nb_txports) { + src_addr = &prox_port_cfg[task->base.tx_params_hw.tx_port_queue[0].port].eth_addr; + targ->flags |= TASK_ARG_SRC_MAC_SET; + plog_info("\t\tCore %d: src mac set from port\n", targ->lconf->id); + memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr)); + } + } + task->runtime_flags = targ->flags; +} + +static struct task_init task_init_l2fwd = { + .mode_str = "l2fwd", + .init = init_task_l2fwd, + .handle = handle_l2fwd_bulk, + .flag_features = TASK_FEATURE_NEVER_DISCARDS|TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .size = sizeof(struct task_l2fwd), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_l2fwd(void) +{ + reg_task(&task_init_l2fwd); +} diff --git a/VNFs/DPPD-PROX/handle_lat.c b/VNFs/DPPD-PROX/handle_lat.c new file mode 100644 index 00000000..0b7ad561 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lat.c @@ -0,0 +1,650 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +//#define LAT_DEBUG + +#include <rte_cycles.h> +#include <stdio.h> +#include <math.h> + +#include "handle_gen.h" +#include "prox_malloc.h" +#include "mbuf_utils.h" +#include "handle_lat.h" +#include "log.h" +#include "task_init.h" +#include "task_base.h" +#include "stats.h" +#include "lconf.h" +#include "quit.h" +#include "eld.h" +#include "prox_shared.h" + +#define DEFAULT_BUCKET_SIZE 10 + +struct lat_info { + uint32_t rx_packet_index; + uint32_t tx_packet_index; + uint32_t tx_err; + uint32_t rx_err; + uint64_t rx_time; + uint64_t tx_time; + uint16_t port_queue_id; +#ifdef LAT_DEBUG + uint16_t id_in_bulk; + uint16_t bulk_size; + uint64_t begin; + uint64_t after; + uint64_t before; +#endif +}; + +struct delayed_latency_entry { + uint32_t rx_packet_idx; + uint64_t pkt_rx_time; + uint64_t pkt_tx_time; + uint64_t rx_time_err; +}; + +struct delayed_latency { + struct delayed_latency_entry entries[64]; +}; + +static struct delayed_latency_entry *delayed_latency_get(struct delayed_latency *delayed_latency, uint32_t rx_packet_idx) +{ + if (delayed_latency->entries[rx_packet_idx % 64].rx_packet_idx == rx_packet_idx) + return &delayed_latency->entries[rx_packet_idx % 64]; + else + return NULL; +} + +static struct delayed_latency_entry *delayed_latency_create(struct delayed_latency *delayed_latency, uint32_t rx_packet_idx) +{ + delayed_latency->entries[rx_packet_idx % 64].rx_packet_idx = rx_packet_idx; + return &delayed_latency->entries[rx_packet_idx % 64]; +} + +struct rx_pkt_meta_data { + uint8_t *hdr; + uint32_t pkt_tx_time; + uint32_t bytes_after_in_bulk; +}; + +struct task_lat { + struct task_base base; + uint64_t limit; + uint64_t rx_packet_index; + uint64_t last_pkts_tsc; + struct delayed_latency delayed_latency; + struct lat_info *latency_buffer; + uint32_t latency_buffer_idx; + uint32_t latency_buffer_size; + uint64_t begin; + uint16_t lat_pos; + uint16_t unique_id_pos; + uint16_t accur_pos; + uint16_t sig_pos; + uint32_t sig; + volatile uint16_t use_lt; /* which lt to use, */ + volatile uint16_t using_lt; /* 0 or 1 depending on which of the 2 measurements are used */ + struct lat_test lt[2]; + struct lat_test *lat_test; + uint32_t generator_count; + struct early_loss_detect *eld; + struct rx_pkt_meta_data *rx_pkt_meta; + FILE *fp_rx; + FILE *fp_tx; +}; + +static uint32_t abs_diff(uint32_t a, uint32_t b) +{ + return a < b? UINT32_MAX - (b - a - 1) : a - b; +} + +struct lat_test *task_lat_get_latency_meassurement(struct task_lat *task) +{ + if (task->use_lt == task->using_lt) + return &task->lt[!task->using_lt]; + return NULL; +} + +void task_lat_use_other_latency_meassurement(struct task_lat *task) +{ + task->use_lt = !task->using_lt; +} + +static void task_lat_update_lat_test(struct task_lat *task) +{ + if (task->use_lt != task->using_lt) { + task->using_lt = task->use_lt; + task->lat_test = &task->lt[task->using_lt]; + task->lat_test->accuracy_limit_tsc = task->limit; + } +} + +static int compare_tx_time(const void *val1, const void *val2) +{ + const struct lat_info *ptr1 = val1; + const struct lat_info *ptr2 = val2; + + return ptr1->tx_time - ptr2->tx_time; +} + +static int compare_queue_id(const void *val1, const void *val2) +{ + return compare_tx_time(val1, val2); +} + +static void fix_latency_buffer_tx_time(struct lat_info *lat, uint32_t count) +{ + uint32_t id, time, old_id = 0, old_time = 0, n_overflow = 0; + + for (uint32_t i = 0; i < count; i++) { + id = lat->port_queue_id; + time = lat->tx_time; + if (id == old_id) { + // Same queue id as previous entry; time should always increase + if (time < old_time) { + n_overflow++; + } + lat->tx_time += UINT32_MAX * n_overflow; + old_time = time; + } else { + // Different queue_id, time starts again at 0 + old_id = id; + old_time = 0; + n_overflow = 0; + } + } +} + +static void task_lat_count_remaining_lost_packets(struct task_lat *task) +{ + struct lat_test *lat_test = task->lat_test; + + for (uint32_t j = 0; j < task->generator_count; j++) { + struct early_loss_detect *eld = &task->eld[j]; + + lat_test->lost_packets += early_loss_detect_count_remaining_loss(eld); + } +} + +static void task_lat_reset_eld(struct task_lat *task) +{ + for (uint32_t j = 0; j < task->generator_count; j++) { + early_loss_detect_reset(&task->eld[j]); + } +} + +static uint64_t lat_latency_buffer_get_min_tsc(struct task_lat *task) +{ + uint64_t min_tsc = UINT64_MAX; + + for (uint32_t i = 0; i < task->latency_buffer_idx; i++) { + if (min_tsc > task->latency_buffer[i].tx_time) + min_tsc = task->latency_buffer[i].tx_time; + } + + return min_tsc << LATENCY_ACCURACY; +} + +static uint64_t lat_info_get_lat_tsc(struct lat_info *lat_info) +{ + uint64_t lat = abs_diff(lat_info->rx_time, lat_info->tx_time); + + return lat << LATENCY_ACCURACY; +} + +static uint64_t lat_info_get_tx_err_tsc(const struct lat_info *lat_info) +{ + return ((uint64_t)lat_info->tx_err) << LATENCY_ACCURACY; +} + +static uint64_t lat_info_get_rx_err_tsc(const struct lat_info *lat_info) +{ + return ((uint64_t)lat_info->rx_err) << LATENCY_ACCURACY; +} + +static uint64_t lat_info_get_rx_tsc(const struct lat_info *lat_info) +{ + return ((uint64_t)lat_info) << LATENCY_ACCURACY; +} + +static uint64_t lat_info_get_tx_tsc(const struct lat_info *lat_info) +{ + return ((uint64_t)lat_info) << LATENCY_ACCURACY; +} + +static void lat_write_latency_to_file(struct task_lat *task) +{ + uint64_t min_tsc; + uint32_t n_loss; + + min_tsc = lat_latency_buffer_get_min_tsc(task); + + // Dumping all packet statistics + fprintf(task->fp_rx, "Latency stats for %u packets, ordered by rx time\n", task->latency_buffer_idx); + fprintf(task->fp_rx, "rx index; queue; tx index; lat (nsec);tx time;\n"); + for (uint32_t i = 0; i < task->latency_buffer_idx ; i++) { + struct lat_info *lat_info = &task->latency_buffer[i]; + uint64_t lat_tsc = lat_info_get_lat_tsc(lat_info); + uint64_t rx_tsc = lat_info_get_rx_tsc(lat_info); + uint64_t tx_tsc = lat_info_get_tx_tsc(lat_info); + + fprintf(task->fp_rx, "%u%d;%d;%ld;%lu;%lu\n", + lat_info->rx_packet_index, + lat_info->port_queue_id, + lat_info->tx_packet_index, + tsc_to_nsec(lat_tsc), + tsc_to_nsec(rx_tsc - min_tsc), + tsc_to_nsec(tx_tsc - min_tsc)); + } + + // To detect dropped packets, we need to sort them based on TX + plogx_info("Sorting packets based on queue_id\n"); + qsort (task->latency_buffer, task->latency_buffer_idx, sizeof(struct lat_info), compare_queue_id); + plogx_info("Adapting tx_time\n"); + fix_latency_buffer_tx_time(task->latency_buffer, task->latency_buffer_idx); + plogx_info("Sorting packets based on tx_time\n"); + qsort (task->latency_buffer, task->latency_buffer_idx, sizeof(struct lat_info), compare_tx_time); + plogx_info("Sorted packets based on tx_time\n"); + + // A packet is marked as dropped if 2 packets received from the same queue are not consecutive + fprintf(task->fp_tx, "Latency stats for %u packets, sorted by tx time\n", task->latency_buffer_idx); + fprintf(task->fp_tx, "queue;tx index; rx index; lat (nsec);tx time; rx time; tx_err;rx_err\n"); + + uint32_t prev_tx_packet_index = -1; + for (uint32_t i = 0; i < task->latency_buffer_idx; i++) { + struct lat_info *lat_info = &task->latency_buffer[i]; + uint64_t lat_tsc = lat_info_get_lat_tsc(lat_info); + uint64_t tx_err_tsc = lat_info_get_tx_err_tsc(lat_info); + uint64_t rx_err_tsc = lat_info_get_rx_err_tsc(lat_info); + uint64_t rx_tsc = lat_info_get_rx_tsc(lat_info); + uint64_t tx_tsc = lat_info_get_tx_tsc(lat_info); + + /* Packet n + 64 delivers the TX error for packet n, + hence the last 64 packets do no have TX error. */ + if (i + 64 >= task->latency_buffer_idx) { + tx_err_tsc = 0; + } + // Log dropped packet + n_loss = lat_info->tx_packet_index - prev_tx_packet_index - 1; + if (n_loss) + fprintf(task->fp_tx, "===> %d;%d;0;0;0;0; lost %d packets <===\n", + lat_info->port_queue_id, + lat_info->tx_packet_index - n_loss, n_loss); + // Log next packet + fprintf(task->fp_tx, "%d;%d;%u;%lu;%lu;%lu;%lu;%lu\n", + lat_info->port_queue_id, + lat_info->tx_packet_index, + lat_info->rx_packet_index, + tsc_to_nsec(lat_tsc), + tsc_to_nsec(tx_tsc - min_tsc), + tsc_to_nsec(rx_tsc - min_tsc), + tsc_to_nsec(tx_err_tsc), + tsc_to_nsec(rx_err_tsc)); +#ifdef LAT_DEBUG + fprintf(task->fp_tx, ";%d from %d;%lu;%lu;%lu", + lat_info->id_in_bulk, + lat_info->bulk_size, + tsc_to_nsec(lat_info->begin - min_tsc), + tsc_to_nsec(lat_info->before - min_tsc), + tsc_to_nsec(lat_info->after - min_tsc)); +#endif + fprintf(task->fp_tx, "\n"); + prev_tx_packet_index = lat_info->tx_packet_index; + } + fflush(task->fp_rx); + fflush(task->fp_tx); + task->latency_buffer_idx = 0; +} + +static void lat_stop(struct task_base *tbase) +{ + struct task_lat *task = (struct task_lat *)tbase; + + if (task->unique_id_pos) { + task_lat_count_remaining_lost_packets(task); + task_lat_reset_eld(task); + } + if (task->latency_buffer) + lat_write_latency_to_file(task); +} + +#ifdef LAT_DEBUG +static void task_lat_store_lat_debug(struct task_lat *task, uint32_t rx_packet_index, uint32_t id_in_bulk, uint32_t bulk_size) +{ + struct lat_info *lat_info = &task->latency_buffer[rx_packet_index]; + + lat_info->bulk_size = bulk_size; + lat_info->id_in_bulk = id_in_bulk; + lat_info->begin = task->begin; + lat_info->before = task->base.aux->tsc_rx.before; + lat_info->after = task->base.aux->tsc_rx.after; +} +#endif + +static void task_lat_store_lat_buf(struct task_lat *task, uint64_t rx_packet_index, struct unique_id *unique_id, uint64_t rx_time, uint64_t tx_time, uint64_t rx_err, uint64_t tx_err) +{ + struct lat_info *lat_info; + uint8_t generator_id = 0; + uint32_t packet_index = 0; + + if (unique_id) + unique_id_get(unique_id, &generator_id, &packet_index); + + /* If unique_id_pos is specified then latency is stored per + packet being sent. Lost packets are detected runtime, and + latency stored for those packets will be 0 */ + lat_info = &task->latency_buffer[task->latency_buffer_idx++]; + lat_info->rx_packet_index = task->latency_buffer_idx - 1; + lat_info->tx_packet_index = packet_index; + lat_info->port_queue_id = generator_id; + lat_info->rx_time = rx_time; + lat_info->tx_time = tx_time; + lat_info->rx_err = rx_err; + lat_info->tx_err = tx_err; +} + +static uint32_t task_lat_early_loss_detect(struct task_lat *task, struct unique_id *unique_id) +{ + struct early_loss_detect *eld; + uint8_t generator_id; + uint32_t packet_index; + + unique_id_get(unique_id, &generator_id, &packet_index); + + if (generator_id >= task->generator_count) + return 0; + + eld = &task->eld[generator_id]; + + return early_loss_detect_add(eld, packet_index); +} + +static uint64_t tsc_extrapolate_backward(uint64_t tsc_from, uint64_t bytes, uint64_t tsc_minimum) +{ + uint64_t tsc = tsc_from - rte_get_tsc_hz()*bytes/1250000000; + if (likely(tsc > tsc_minimum)) + return tsc; + else + return tsc_minimum; +} + +static void lat_test_histogram_add(struct lat_test *lat_test, uint64_t lat_tsc) +{ + uint64_t bucket_id = (lat_tsc >> lat_test->bucket_size); + size_t bucket_count = sizeof(lat_test->buckets)/sizeof(lat_test->buckets[0]); + + bucket_id = bucket_id < bucket_count? bucket_id : bucket_count; + lat_test->buckets[bucket_id]++; +} + +static void lat_test_add_lost(struct lat_test *lat_test, uint64_t lost_packets) +{ + lat_test->lost_packets += lost_packets; +} + +static void lat_test_add_latency(struct lat_test *lat_test, uint64_t lat_tsc, uint64_t error) +{ + lat_test->tot_all_pkts++; + + if (error > lat_test->accuracy_limit_tsc) + return; + lat_test->tot_pkts++; + + lat_test->tot_lat += lat_tsc; + lat_test->tot_lat_error += error; + + /* (a +- b)^2 = a^2 +- (2ab + b^2) */ + lat_test->var_lat += lat_tsc * lat_tsc; + lat_test->var_lat_error += 2 * lat_tsc * error; + lat_test->var_lat_error += error * error; + + if (lat_tsc > lat_test->max_lat) { + lat_test->max_lat = lat_tsc; + lat_test->max_lat_error = error; + } + if (lat_tsc < lat_test->min_lat) { + lat_test->min_lat = lat_tsc; + lat_test->min_lat_error = error; + } + +#ifdef LATENCY_HISTOGRAM + lat_test_histogram_add(lat_test, lat_tsc); +#endif +} + +static int task_lat_can_store_latency(struct task_lat *task) +{ + return task->latency_buffer_idx < task->latency_buffer_size; +} + +static void task_lat_store_lat(struct task_lat *task, uint64_t rx_packet_index, uint64_t rx_time, uint64_t tx_time, uint64_t rx_error, uint64_t tx_error, struct unique_id *unique_id) +{ + if (tx_time == 0) + return; + uint32_t lat_tsc = abs_diff(rx_time, tx_time) << LATENCY_ACCURACY; + + lat_test_add_latency(task->lat_test, lat_tsc, rx_error + tx_error); + + if (task_lat_can_store_latency(task)) { + task_lat_store_lat_buf(task, rx_packet_index, unique_id, rx_time, tx_time, rx_error, tx_error); + } +} + +static int handle_lat_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lat *task = (struct task_lat *)tbase; + uint64_t rx_time_err; + + uint32_t pkt_rx_time, pkt_tx_time; + + if (n_pkts == 0) { + task->begin = tbase->aux->tsc_rx.before; + return 0; + } + + task_lat_update_lat_test(task); + + const uint64_t rx_tsc = tbase->aux->tsc_rx.after; + uint32_t tx_time_err = 0; + + /* Go once through all received packets and read them. If + packet has just been modified by another core, the cost of + latency will be partialy amortized though the bulk size */ + for (uint16_t j = 0; j < n_pkts; ++j) { + struct rte_mbuf *mbuf = mbufs[j]; + task->rx_pkt_meta[j].hdr = rte_pktmbuf_mtod(mbuf, uint8_t *); + } + for (uint16_t j = 0; j < n_pkts; ++j) { + } + + if (task->sig) { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (*(uint32_t *)(task->rx_pkt_meta[j].hdr + task->sig_pos) == task->sig) + task->rx_pkt_meta[j].pkt_tx_time = *(uint32_t *)(task->rx_pkt_meta[j].hdr + task->lat_pos); + else + task->rx_pkt_meta[j].pkt_tx_time = 0; + } + } else { + for (uint16_t j = 0; j < n_pkts; ++j) { + task->rx_pkt_meta[j].pkt_tx_time = *(uint32_t *)(task->rx_pkt_meta[j].hdr + task->lat_pos); + } + } + + uint32_t bytes_total_in_bulk = 0; + // Find RX time of first packet, for RX accuracy + for (uint16_t j = 0; j < n_pkts; ++j) { + uint16_t flipped = n_pkts - 1 - j; + + task->rx_pkt_meta[flipped].bytes_after_in_bulk = bytes_total_in_bulk; + bytes_total_in_bulk += mbuf_wire_size(mbufs[flipped]); + } + + pkt_rx_time = tsc_extrapolate_backward(rx_tsc, task->rx_pkt_meta[0].bytes_after_in_bulk, task->last_pkts_tsc) >> LATENCY_ACCURACY; + if ((uint32_t)((task->begin >> LATENCY_ACCURACY)) > pkt_rx_time) { + // Extrapolation went up to BEFORE begin => packets were stuck in the NIC but we were not seeing them + rx_time_err = pkt_rx_time - (uint32_t)(task->last_pkts_tsc >> LATENCY_ACCURACY); + } else { + rx_time_err = pkt_rx_time - (uint32_t)(task->begin >> LATENCY_ACCURACY); + } + + struct unique_id *unique_id = NULL; + struct delayed_latency_entry *delayed_latency_entry; + + for (uint16_t j = 0; j < n_pkts; ++j) { + struct rx_pkt_meta_data *rx_pkt_meta = &task->rx_pkt_meta[j]; + uint8_t *hdr = rx_pkt_meta->hdr; + + pkt_rx_time = tsc_extrapolate_backward(rx_tsc, rx_pkt_meta->bytes_after_in_bulk, task->last_pkts_tsc) >> LATENCY_ACCURACY; + pkt_tx_time = rx_pkt_meta->pkt_tx_time; + + if (task->unique_id_pos) { + unique_id = (struct unique_id *)(hdr + task->unique_id_pos); + + uint32_t n_loss = task_lat_early_loss_detect(task, unique_id); + lat_test_add_lost(task->lat_test, n_loss); + } + + /* If accuracy is enabled, latency is reported with a + delay of 64 packets since the generator puts the + accuracy for packet N into packet N + 64. The delay + ensures that all reported latencies have both rx + and tx error. */ + if (task->accur_pos) { + tx_time_err = *(uint32_t *)(hdr + task->accur_pos); + + delayed_latency_entry = delayed_latency_get(&task->delayed_latency, task->rx_packet_index - 64); + + if (delayed_latency_entry) { + task_lat_store_lat(task, + task->rx_packet_index, + delayed_latency_entry->pkt_rx_time, + delayed_latency_entry->pkt_tx_time, + delayed_latency_entry->rx_time_err, + tx_time_err, + unique_id); + } + + delayed_latency_entry = delayed_latency_create(&task->delayed_latency, task->rx_packet_index); + delayed_latency_entry->pkt_rx_time = pkt_rx_time; + delayed_latency_entry->pkt_tx_time = pkt_tx_time; + delayed_latency_entry->rx_time_err = rx_time_err; + } else { + task_lat_store_lat(task, + task->rx_packet_index, + pkt_rx_time, + pkt_tx_time, + 0, + 0, + unique_id); + } + task->rx_packet_index++; + } + int ret; + ret = task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); + task->begin = tbase->aux->tsc_rx.before; + task->last_pkts_tsc = tbase->aux->tsc_rx.after; + return ret; +} + +static void init_task_lat_latency_buffer(struct task_lat *task, uint32_t core_id) +{ + const int socket_id = rte_lcore_to_socket_id(core_id); + char name[256]; + size_t latency_buffer_mem_size = 0; + + if (task->latency_buffer_size > UINT32_MAX - MAX_RING_BURST) + task->latency_buffer_size = UINT32_MAX - MAX_RING_BURST; + + latency_buffer_mem_size = sizeof(struct lat_info) * task->latency_buffer_size; + + task->latency_buffer = prox_zmalloc(latency_buffer_mem_size, socket_id); + PROX_PANIC(task->latency_buffer == NULL, "Failed to allocate %ld kbytes for %s\n", latency_buffer_mem_size / 1024, name); + + sprintf(name, "latency.rx_%d.txt", core_id); + task->fp_rx = fopen(name, "w+"); + PROX_PANIC(task->fp_rx == NULL, "Failed to open %s\n", name); + + sprintf(name, "latency.tx_%d.txt", core_id); + task->fp_tx = fopen(name, "w+"); + PROX_PANIC(task->fp_tx == NULL, "Failed to open %s\n", name); +} + +static void task_lat_init_eld(struct task_lat *task, uint8_t socket_id) +{ + uint8_t *generator_count = prox_sh_find_system("generator_count"); + size_t eld_mem_size; + + if (generator_count == NULL) + task->generator_count = 0; + else + task->generator_count = *generator_count; + + eld_mem_size = sizeof(task->eld[0]) * task->generator_count; + task->eld = prox_zmalloc(eld_mem_size, socket_id); +} + +void task_lat_set_accuracy_limit(struct task_lat *task, uint32_t accuracy_limit_nsec) +{ + task->limit = nsec_to_tsc(accuracy_limit_nsec); +} + +static void init_task_lat(struct task_base *tbase, struct task_args *targ) +{ + struct task_lat *task = (struct task_lat *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->lat_pos = targ->lat_pos; + task->accur_pos = targ->accur_pos; + task->unique_id_pos = targ->packet_id_pos; + task->latency_buffer_size = targ->latency_buffer_size; + + if (task->latency_buffer_size) { + init_task_lat_latency_buffer(task, targ->lconf->id); + } + + if (targ->bucket_size < LATENCY_ACCURACY) { + targ->bucket_size = DEFAULT_BUCKET_SIZE; + } + + task->lt[0].bucket_size = targ->bucket_size - LATENCY_ACCURACY; + task->lt[1].bucket_size = targ->bucket_size - LATENCY_ACCURACY; + if (task->unique_id_pos) { + task_lat_init_eld(task, socket_id); + task_lat_reset_eld(task); + } + task->lat_test = &task->lt[task->using_lt]; + + task_lat_set_accuracy_limit(task, targ->accuracy_limit_nsec); + task->rx_pkt_meta = prox_zmalloc(MAX_RX_PKT_ALL * sizeof(*task->rx_pkt_meta), socket_id); + PROX_PANIC(task->rx_pkt_meta == NULL, "unable to allocate memory to store RX packet meta data"); +} + +static struct task_init task_init_lat = { + .mode_str = "lat", + .init = init_task_lat, + .handle = handle_lat_bulk, + .stop = lat_stop, + .flag_features = TASK_FEATURE_TSC_RX | TASK_FEATURE_RX_ALL | TASK_FEATURE_ZERO_RX | TASK_FEATURE_NEVER_DISCARDS, + .size = sizeof(struct task_lat) +}; + +__attribute__((constructor)) static void reg_task_lat(void) +{ + reg_task(&task_init_lat); +} diff --git a/VNFs/DPPD-PROX/handle_lat.h b/VNFs/DPPD-PROX/handle_lat.h new file mode 100644 index 00000000..a832a641 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lat.h @@ -0,0 +1,189 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_LAT_H_ +#define _HANDLE_LAT_H_ + +#include <stdio.h> +#include <math.h> +#include <string.h> + +#include "task_base.h" +#include "clock.h" + +#define MAX_PACKETS_FOR_LATENCY 64 +#define LATENCY_ACCURACY 1 + +struct lat_test { + uint64_t tot_all_pkts; + uint64_t tot_pkts; + uint64_t max_lat; + uint64_t min_lat; + uint64_t tot_lat; + unsigned __int128 var_lat; /* variance */ + uint64_t accuracy_limit_tsc; + + uint64_t max_lat_error; + uint64_t min_lat_error; + uint64_t tot_lat_error; + unsigned __int128 var_lat_error; + + uint64_t buckets[128]; + uint64_t bucket_size; + uint64_t lost_packets; +}; + +static struct time_unit lat_test_get_accuracy_limit(struct lat_test *lat_test) +{ + return tsc_to_time_unit(lat_test->accuracy_limit_tsc); +} + +static struct time_unit_err lat_test_get_avg(struct lat_test *lat_test) +{ + uint64_t tsc; + uint64_t tsc_error; + + tsc = lat_test->tot_lat/lat_test->tot_pkts; + tsc_error = lat_test->tot_lat_error/lat_test->tot_pkts; + + struct time_unit_err ret = { + .time = tsc_to_time_unit(tsc), + .error = tsc_to_time_unit(tsc_error), + }; + + return ret; +} + +static struct time_unit_err lat_test_get_min(struct lat_test *lat_test) +{ + struct time_unit_err ret = { + .time = tsc_to_time_unit(lat_test->min_lat), + .error = tsc_to_time_unit(lat_test->min_lat_error), + }; + + return ret; +} + +static struct time_unit_err lat_test_get_max(struct lat_test *lat_test) +{ + struct time_unit_err ret = { + .time = tsc_to_time_unit(lat_test->max_lat), + .error = tsc_to_time_unit(lat_test->max_lat_error), + }; + + return ret; +} + +static struct time_unit_err lat_test_get_stddev(struct lat_test *lat_test) +{ + unsigned __int128 avg_tsc = lat_test->tot_lat/lat_test->tot_pkts; + unsigned __int128 avg_tsc_squared = avg_tsc * avg_tsc; + unsigned __int128 avg_squares_tsc = lat_test->var_lat/lat_test->tot_pkts; + + /* The assumption is that variance fits into 64 bits, meaning + that standard deviation fits into 32 bits. In other words, + the assumption is that the standard deviation is not more + than approximately 1 second. */ + uint64_t var_tsc = avg_squares_tsc - avg_tsc_squared; + uint64_t stddev_tsc = sqrt(var_tsc); + + unsigned __int128 avg_tsc_error = lat_test->tot_lat_error / lat_test->tot_pkts; + unsigned __int128 avg_tsc_squared_error = 2 * avg_tsc * avg_tsc_error + avg_tsc_error * avg_tsc_error; + unsigned __int128 avg_squares_tsc_error = lat_test->var_lat_error / lat_test->tot_pkts; + + uint64_t var_tsc_error = avg_squares_tsc_error + avg_tsc_squared_error; + + /* sqrt(a+-b) = sqrt(a) +- (-sqrt(a) + sqrt(a + b)) */ + + uint64_t stddev_tsc_error = - stddev_tsc + sqrt(var_tsc + var_tsc_error); + + struct time_unit_err ret = { + .time = tsc_to_time_unit(stddev_tsc), + .error = tsc_to_time_unit(stddev_tsc_error), + }; + + return ret; +} + +static void _lat_test_histogram_combine(struct lat_test *dst, struct lat_test *src) +{ + for (size_t i = 0; i < sizeof(dst->buckets)/sizeof(dst->buckets[0]); ++i) + dst->buckets[i] += src->buckets[i]; +} + +static void lat_test_combine(struct lat_test *dst, struct lat_test *src) +{ + dst->tot_all_pkts += src->tot_all_pkts; + + dst->tot_pkts += src->tot_pkts; + + dst->tot_lat += src->tot_lat; + dst->tot_lat_error += src->tot_lat_error; + + /* (a +- b)^2 = a^2 +- (2ab + b^2) */ + dst->var_lat += src->var_lat; + dst->var_lat_error += src->var_lat_error; + + if (src->max_lat > dst->max_lat) { + dst->max_lat = src->max_lat; + dst->max_lat_error = src->max_lat_error; + } + if (src->min_lat < dst->min_lat) { + dst->min_lat = src->min_lat; + dst->min_lat_error = src->min_lat_error; + } + + if (src->accuracy_limit_tsc > dst->accuracy_limit_tsc) + dst->accuracy_limit_tsc = src->accuracy_limit_tsc; + dst->lost_packets += src->lost_packets; + +#ifdef LATENCY_HISTOGRAM + _lat_test_histogram_combine(dst, src); +#endif +} + +static void lat_test_reset(struct lat_test *lat_test) +{ + lat_test->tot_all_pkts = 0; + lat_test->tot_pkts = 0; + lat_test->max_lat = 0; + lat_test->min_lat = -1; + lat_test->tot_lat = 0; + lat_test->var_lat = 0; + lat_test->max_lat_error = 0; + lat_test->min_lat_error = 0; + lat_test->tot_lat_error = 0; + lat_test->var_lat_error = 0; + lat_test->accuracy_limit_tsc = 0; + + lat_test->lost_packets = 0; + + memset(lat_test->buckets, 0, sizeof(lat_test->buckets)); +} + +static void lat_test_copy(struct lat_test *dst, struct lat_test *src) +{ + if (src->tot_all_pkts) + memcpy(dst, src, sizeof(struct lat_test)); +} + +struct task_lat; + +struct lat_test *task_lat_get_latency_meassurement(struct task_lat *task); +void task_lat_use_other_latency_meassurement(struct task_lat *task); +void task_lat_set_accuracy_limit(struct task_lat *task, uint32_t accuracy_limit_nsec); + +#endif /* _HANDLE_LAT_H_ */ diff --git a/VNFs/DPPD-PROX/handle_lb_5tuple.c b/VNFs/DPPD-PROX/handle_lb_5tuple.c new file mode 100644 index 00000000..ae973f1c --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_5tuple.c @@ -0,0 +1,143 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_hash.h> +#include <rte_ether.h> +#include <rte_memcpy.h> +#include <rte_mbuf.h> +#include <rte_ip.h> +#include <rte_tcp.h> +#include <rte_udp.h> +#include <rte_version.h> +#include <rte_byteorder.h> + +#include "handle_lb_5tuple.h" +#include "prox_malloc.h" +#include "prox_lua.h" +#include "prox_lua_types.h" +#include "etypes.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "prefetch.h" +#include "prox_globals.h" +#include "defines.h" +#include "quit.h" + +#define BYTE_VALUE_MAX 256 +#define ALL_32_BITS 0xffffffff +#define BIT_8_TO_15 0x0000ff00 + +#define HASH_MAX_SIZE 4*8*1024*1024 + +struct task_lb_5tuple { + struct task_base base; + uint32_t runtime_flags; + struct rte_hash *lookup_hash; + uint8_t out_if[HASH_MAX_SIZE] __rte_cache_aligned; +}; + +static __m128i mask0; +static inline uint8_t get_ipv4_dst_port(struct task_lb_5tuple *task, void *ipv4_hdr, uint8_t portid, struct rte_hash * ipv4_l3fwd_lookup_struct) +{ + int ret = 0; + union ipv4_5tuple_host key; + + ipv4_hdr = (uint8_t *)ipv4_hdr + offsetof(struct ipv4_hdr, time_to_live); + __m128i data = _mm_loadu_si128((__m128i*)(ipv4_hdr)); + /* Get 5 tuple: dst port, src port, dst IP address, src IP address and protocol */ + key.xmm = _mm_and_si128(data, mask0); + + /* Get 5 tuple: dst port, src port, dst IP address, src IP address and protocol */ + /* + rte_mov16(&key.pad0, ipv4_hdr); + key.pad0 = 0; + key.pad1 = 0; + */ + /* Find destination port */ + ret = rte_hash_lookup(ipv4_l3fwd_lookup_struct, (const void *)&key); + return (uint8_t)((ret < 0)? portid : task->out_if[ret]); +} + +static inline uint8_t handle_lb_5tuple(struct task_lb_5tuple *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *eth_hdr; + struct ipv4_hdr *ipv4_hdr; + + eth_hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + + switch (eth_hdr->ether_type) { + case ETYPE_IPv4: + /* Handle IPv4 headers.*/ + ipv4_hdr = (struct ipv4_hdr *) (eth_hdr + 1); + return get_ipv4_dst_port(task, ipv4_hdr, OUT_DISCARD, task->lookup_hash); + default: + return OUT_DISCARD; + } +} + +static int handle_lb_5tuple_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_5tuple *task = (struct task_lb_5tuple *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_lb_5tuple(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_lb_5tuple(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void init_task_lb_5tuple(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_5tuple *task = (struct task_lb_5tuple *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + mask0 = _mm_set_epi32(ALL_32_BITS, ALL_32_BITS, ALL_32_BITS, BIT_8_TO_15); + + uint8_t *out_table = task->out_if; + int ret = lua_to_tuples(prox_lua(), GLOBAL, "tuples", socket_id, &task->lookup_hash, &out_table); + PROX_PANIC(ret, "Failed to read tuples from config\n"); + + task->runtime_flags = targ->flags; +} + +static struct task_init task_init_lb_5tuple = { + .mode_str = "lb5tuple", + .init = init_task_lb_5tuple, + .handle = handle_lb_5tuple_bulk, + .flag_features = TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, + .size = sizeof(struct task_lb_5tuple), +}; + +__attribute__((constructor)) static void reg_task_lb_5tuple(void) +{ + reg_task(&task_init_lb_5tuple); +} diff --git a/VNFs/DPPD-PROX/handle_lb_5tuple.h b/VNFs/DPPD-PROX/handle_lb_5tuple.h new file mode 100644 index 00000000..bb830fa7 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_5tuple.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_LB_TUP_H_ +#define _HANDLE_LB_TUP_H_ + +union ipv4_5tuple_host { + struct { + uint8_t pad0; + uint8_t proto; + uint16_t pad1; + uint32_t ip_src; + uint32_t ip_dst; + uint16_t port_src; + uint16_t port_dst; + }; + __m128i xmm; +}; + +#endif /* _HANDLE_LB_TUP_H_ */ diff --git a/VNFs/DPPD-PROX/handle_lb_net.c b/VNFs/DPPD-PROX/handle_lb_net.c new file mode 100644 index 00000000..878b8158 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_net.c @@ -0,0 +1,577 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> + +#include <rte_mbuf.h> +#include <rte_ip.h> +#include <rte_table_hash.h> +#include <rte_byteorder.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "handle_lb_net.h" +#include "task_base.h" +#include "defines.h" +#include "tx_pkt.h" +#include "log.h" +#include "stats.h" +#include "mpls.h" +#include "etypes.h" +#include "gre.h" +#include "prefetch.h" +#include "qinq.h" +#include "hash_utils.h" +#include "quit.h" +#include "flow_iter.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +struct task_lb_net { + struct task_base base; + uint16_t qinq_tag; + uint8_t bit_mask; + uint8_t nb_worker_threads; + uint8_t worker_byte_offset_ipv4; + uint8_t worker_byte_offset_ipv6; + uint8_t runtime_flags; +}; + +struct task_lb_net_lut { + struct task_base base; + uint8_t nb_worker_threads; + uint8_t runtime_flags; + struct rte_table_hash *worker_hash_table; + uint8_t *worker_lut; + uint32_t keys[64]; + struct rte_mbuf *fake_packets[64]; +}; + +static inline uint8_t handle_lb_net(struct task_lb_net *task, struct rte_mbuf *mbuf); +static inline int extract_gre_key(struct task_lb_net_lut *task, uint32_t *key, struct rte_mbuf *mbuf); + +static struct rte_table_hash *setup_gre_to_wt_lookup(struct task_args *targ, uint8_t n_workers, int socket_id) +{ + uint32_t gre_id, rss; + void* entry_in_hash; + int r, key_found = 0; + struct rte_table_hash *ret; + uint32_t count = 0; + + for (int i = 0; i < n_workers; ++i) { + struct core_task ct = targ->core_task_set[0].core_task[i]; + struct task_args *t = core_targ_get(ct.core, ct.task); + + struct flow_iter *it = &t->task_init->flow_iter; + + PROX_PANIC(t->task_init->flow_iter.beg == NULL, + "Load distributor can't find flows owned by destination worker %d\n", i); + + for (it->beg(it, t); !it->is_end(it, t); it->next(it, t)) { + count++; + } + } + + struct rte_table_hash_ext_params table_hash_params = { + .key_size = 4, + .n_keys = count, + .n_buckets = count, + .n_buckets_ext = count >> 1, + .f_hash = hash_crc32, + .seed = 0, + .signature_offset = HASH_METADATA_OFFSET(0), + .key_offset = HASH_METADATA_OFFSET(0), + }; + + ret = rte_table_hash_ext_dosig_ops.f_create(&table_hash_params, socket_id, sizeof(uint8_t)); + + for (int i = 0; i < n_workers; ++i) { + struct core_task ct = targ->core_task_set[0].core_task[i]; + struct task_args *t = core_targ_get(ct.core, ct.task); + + PROX_PANIC(t->task_init->flow_iter.beg == NULL, + "Load distributor can't find flows owned by destination worker %d\n", i); + + struct flow_iter *it = &t->task_init->flow_iter; + + for (it->beg(it, t); !it->is_end(it, t); it->next(it, t)) { + uint32_t gre_id = it->get_gre_id(it, t); + uint8_t dst = i; + + r = rte_table_hash_ext_dosig_ops.f_add(ret, &gre_id, &dst, &key_found, &entry_in_hash); + if (r) { + plog_err("Failed to add gre_id = %x, dest worker = %u\n", gre_id, i); + } + else { + plog_dbg("Core %u added: gre_id %x, dest woker = %u\n", targ->lconf->id, gre_id, i); + } + } + } + return ret; +} + +static uint8_t *setup_wt_indexed_table(struct task_args *targ, uint8_t n_workers, int socket_id) +{ + uint32_t gre_id, rss; + uint32_t max_gre_id = 0; + uint8_t queue; + uint8_t *ret = NULL; + void* entry_in_hash; + int key_found = 0; + + for (int i = 0; i < n_workers; ++i) { + struct core_task ct = targ->core_task_set[0].core_task[i]; + struct task_args *t = core_targ_get(ct.core, ct.task); + + struct flow_iter *it = &t->task_init->flow_iter; + + PROX_PANIC(t->task_init->flow_iter.beg == NULL, + "Load distributor can't find flows owned by destination worker %d\n", i); + + for (it->beg(it, t); !it->is_end(it, t); it->next(it, t)) { + uint32_t gre_id = it->get_gre_id(it, t); + if (gre_id > max_gre_id) + max_gre_id = gre_id; + } + } + + PROX_PANIC(max_gre_id == 0, "Failed to get maximum GRE ID from workers"); + + ret = prox_zmalloc(1 + max_gre_id, socket_id); + PROX_PANIC(ret == NULL, "Failed to allocate worker_lut\n"); + + for (int i = 0; i < n_workers; ++i) { + struct core_task ct = targ->core_task_set[0].core_task[i]; + struct task_args *t = core_targ_get(ct.core, ct.task); + + PROX_PANIC(t->task_init->flow_iter.beg == NULL, + "Load distributor can't find flows owned by destination worker %d\n", i); + + struct flow_iter *it = &t->task_init->flow_iter; + + for (it->beg(it, t); !it->is_end(it, t); it->next(it, t)) { + uint32_t gre_id = it->get_gre_id(it, t); + uint8_t dst = i; + + ret[gre_id] = dst; + } + } + return ret; +} + +static void init_task_lb_net(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_net *task = (struct task_lb_net *)tbase; + + task->qinq_tag = targ->qinq_tag; + task->runtime_flags = targ->runtime_flags; + task->worker_byte_offset_ipv6 = 23; + task->worker_byte_offset_ipv4 = 15; + task->nb_worker_threads = targ->nb_worker_threads; + /* The optimal configuration is when the number of worker threads + is a power of 2. In that case, a bit_mask can be used. Setting + the bitmask to 0xff disables the "optimal" usage of bitmasks + and the actual number of worker threads will be used instead. */ + task->bit_mask = rte_is_power_of_2(targ->nb_worker_threads) ? targ->nb_worker_threads - 1 : 0xff; +} + +static void init_task_lb_net_lut(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_net_lut *task = (struct task_lb_net_lut *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->runtime_flags = targ->runtime_flags; + task->nb_worker_threads = targ->nb_worker_threads; + for (uint32_t i = 0; i < 64; ++i) { + task->fake_packets[i] = (struct rte_mbuf*)((uint8_t*)&task->keys[i] - sizeof (struct rte_mbuf)); + } + + task->worker_hash_table = setup_gre_to_wt_lookup(targ, task->nb_worker_threads, socket_id); +} + +static void init_task_lb_net_indexed_table(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_net_lut *task = (struct task_lb_net_lut *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->runtime_flags = targ->runtime_flags; + task->nb_worker_threads = targ->nb_worker_threads; + + task->worker_lut = setup_wt_indexed_table(targ, task->nb_worker_threads, socket_id); +} + +static int handle_lb_net_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_net *task = (struct task_lb_net *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_lb_net(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + + for (; j < n_pkts; ++j) { + out[j] = handle_lb_net(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int handle_lb_net_lut_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_net_lut *task = (struct task_lb_net_lut *)tbase; + uint16_t not_dropped = 0; + uint8_t out[MAX_PKT_BURST]; + // process packet, i.e. decide if the packet has to be dropped or not and where the packet has to go + uint16_t j; + prefetch_first(mbufs, n_pkts); + + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + uint8_t *wt[MAX_PKT_BURST]; + uint64_t lookup_hit_mask = 0; + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + if (extract_gre_key(task, &task->keys[j], mbufs[j])) { + // Packet will be dropped after lookup + pkts_mask &= ~(1 << j); + out[j] = OUT_DISCARD; + } + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + if (extract_gre_key(task, &task->keys[j], mbufs[j])) { + pkts_mask &= ~(1 << j); + out[j] = OUT_DISCARD; + rte_prefetch0(RTE_MBUF_METADATA_UINT8_PTR(mbufs[j], 0)); + } + } +#endif + // keys have been extracted for all packets, now do the lookup + rte_table_hash_ext_dosig_ops.f_lookup(task->worker_hash_table, task->fake_packets, pkts_mask, &lookup_hit_mask, (void**)wt); + /* mbufs now contains the packets that have not been dropped */ + if (likely(lookup_hit_mask == RTE_LEN2MASK(n_pkts, uint64_t))) { + for (j = 0; j < n_pkts; ++j) { + out[j] = *wt[j]; + } + } + else { + for (j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + plog_warn("Packet %d keys %x can not be sent to worker thread => dropped\n", j, task->keys[j]); + out[j] = OUT_DISCARD; + } + else { + out[j] = *wt[j]; + } + } + } + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int handle_lb_net_indexed_table_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_net_lut *task = (struct task_lb_net_lut *)tbase; + uint8_t out[MAX_PKT_BURST]; + // process packet, i.e. decide if the packet has to be dropped or not and where the packet has to go + uint16_t j; + uint32_t gre_id; + prefetch_first(mbufs, n_pkts); + + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + if (extract_gre_key(task, &gre_id, mbufs[j])) { + // Packet will be dropped after lookup + pkts_mask &= ~(1 << j); + out[j] = OUT_DISCARD; + } else { + out[j] = task->worker_lut[rte_bswap32(gre_id)]; + } + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + if (extract_gre_key(task, &gre_id, mbufs[j])) { + pkts_mask &= ~(1 << j); + out[j] = OUT_DISCARD; + } else { + out[j] = task->worker_lut[rte_bswap32(gre_id)]; + } + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static inline uint8_t worker_from_mask(struct task_lb_net *task, uint32_t val) +{ + if (task->bit_mask != 0xff) { + return val & task->bit_mask; + } + else { + return val % task->nb_worker_threads; + } +} + +static inline int extract_gre_key(struct task_lb_net_lut *task, uint32_t *key, struct rte_mbuf *mbuf) +{ + // For all packets, one by one, remove MPLS tag if any and fills in keys used by "fake" packets + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + // Check for MPLS TAG + struct ipv4_hdr *ip; + if (peth->ether_type == ETYPE_MPLSU) { + struct mpls_hdr *mpls = (struct mpls_hdr *)(peth + 1); + uint32_t mpls_len = 0; + while (!(mpls->bytes & 0x00010000)) { + mpls++; + mpls_len += sizeof(struct mpls_hdr); + } + mpls_len += sizeof(struct mpls_hdr); + ip = (struct ipv4_hdr *)(mpls + 1); + switch (ip->version_ihl >> 4) { + case 4: + // Remove MPLS Tag if requested + if (task->runtime_flags & TASK_MPLS_TAGGING) { + peth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, mpls_len); + peth->ether_type = ETYPE_IPv4; + } + break; + case 6: + plog_warn("IPv6 not supported in this mode\n"); + return 1;; + default: + plog_warn("Unexpected IP version %d\n", ip->version_ihl >> 4); + return 1; + } + } + else { + ip = (struct ipv4_hdr *)(peth + 1); + } + // Entry point for the packet => check for packet validity + // => do not use extract_key_core(mbufs[j], &task->keys[j]); + // + if (likely(ip->next_proto_id == IPPROTO_GRE)) { + struct gre_hdr *pgre = (struct gre_hdr *)(ip + 1); + if (likely(pgre->bits & GRE_KEY_PRESENT)) { + uint32_t gre_id; + if (pgre->bits & (GRE_CRC_PRESENT | GRE_ROUTING_PRESENT)) { + // gre_id = *((uint32_t *)((uint8_t *)pgre + 8)); + *key = *(uint32_t *)((uint8_t *)pgre + 8); + } + else { + // gre_id = *((uint32_t *)((uint8_t *)pgre + 4)); + *key = *(uint32_t *)((uint8_t *)pgre + 4); + } + } + else { + plog_warn("Key not present\n"); + return 1; + } + } + else { + plog_warn("Invalid protocol: GRE xas expected, got 0x%x\n", ip->next_proto_id); + return 1; + } + return 0; +} + +static inline uint8_t lb_ip4(struct task_lb_net *task, struct ipv4_hdr *ip) +{ + if (unlikely(ip->version_ihl >> 4 != 4)) { + plog_warn("Expected to receive IPv4 packet but IP version was %d\n", + ip->version_ihl >> 4); + return OUT_DISCARD; + } + + if (ip->next_proto_id == IPPROTO_GRE) { + struct gre_hdr *pgre = (struct gre_hdr *)(ip + 1); + + if (pgre->bits & GRE_KEY_PRESENT) { + uint32_t gre_id; + if (pgre->bits & (GRE_CRC_PRESENT | GRE_ROUTING_PRESENT)) { + gre_id = *((uint32_t *)((uint8_t *)pgre + 8)); + } + else { + gre_id = *((uint32_t *)((uint8_t *)pgre + 4)); + } + + gre_id = rte_be_to_cpu_32(gre_id) & 0xFFFFFFF; + uint8_t worker = worker_from_mask(task, gre_id); + plogx_dbg("gre_id = %u worker = %u\n", gre_id, worker); + return worker + task->nb_worker_threads * IPV4; + } + else { + plog_warn("Key not present\n"); + return OUT_DISCARD; + } + } + else if (ip->next_proto_id == IPPROTO_UDP) { + uint8_t worker = worker_from_mask(task, rte_bswap32(ip->dst_addr)); + return worker + task->nb_worker_threads * IPV4; + } + return OUT_DISCARD; +} + +static inline uint8_t lb_ip6(struct task_lb_net *task, struct ipv6_hdr *ip) +{ + if (unlikely((*(uint8_t*)ip) >> 4 != 6)) { + plog_warn("Expected to receive IPv6 packet but IP version was %d\n", + *(uint8_t*)ip >> 4); + return OUT_DISCARD; + } + + uint8_t worker = worker_from_mask(task, *((uint8_t *)ip + task->worker_byte_offset_ipv6)); + return worker + task->nb_worker_threads * IPV6; +} + +static inline uint8_t lb_mpls(struct task_lb_net *task, struct ether_hdr *peth, struct rte_mbuf *mbuf) +{ + struct mpls_hdr *mpls = (struct mpls_hdr *)(peth + 1); + uint32_t mpls_len = 0; + while (!(mpls->bytes & 0x00010000)) { + mpls++; + mpls_len += sizeof(struct mpls_hdr); + } + mpls_len += sizeof(struct mpls_hdr); + struct ipv4_hdr *ip = (struct ipv4_hdr *)(mpls + 1); + + switch (ip->version_ihl >> 4) { + case 4: + if (task->runtime_flags & TASK_MPLS_TAGGING) { + peth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, mpls_len); + peth->ether_type = ETYPE_IPv4; + } + return lb_ip4(task, ip); + case 6: + if (task->runtime_flags & TASK_MPLS_TAGGING) { + peth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, mpls_len); + peth->ether_type = ETYPE_IPv6; + } + return lb_ip6(task, (struct ipv6_hdr *)ip); + default: + plogd_warn(mbuf, "Failed Decoding MPLS Packet - neither IPv4 neither IPv6: version %u for packet : \n", ip->version_ihl); + return OUT_DISCARD; + } +} + +static inline uint8_t lb_qinq(struct task_lb_net *task, struct qinq_hdr *qinq) +{ + if (qinq->cvlan.eth_proto != ETYPE_VLAN) { + plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto); + return OUT_DISCARD; + } + uint32_t qinq_tags = rte_bswap16(qinq->cvlan.vlan_tci & 0xFF0F); + return worker_from_mask(task, qinq_tags); +} + +static inline uint8_t handle_lb_net(struct task_lb_net *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + const uint16_t len = rte_pktmbuf_pkt_len(mbuf); + if (len < 60) { + plogd_warn(mbuf, "Unexpected frame len = %d for packet : \n", len); + return OUT_DISCARD; + } + + switch (peth->ether_type) { + case ETYPE_MPLSU: + return lb_mpls(task, peth, mbuf); + case ETYPE_8021ad: + return lb_qinq(task, (struct qinq_hdr *)peth); + case ETYPE_IPv4: + return lb_ip4(task, (struct ipv4_hdr *)(peth + 1)); + case ETYPE_IPv6: + return lb_ip6(task, (struct ipv6_hdr *)(peth + 1)); + case ETYPE_LLDP: + return OUT_DISCARD; + default: + if (peth->ether_type == task->qinq_tag) + return lb_qinq(task, (struct qinq_hdr *)peth); + plogd_warn(mbuf, "Unexpected frame Ether type = %#06x for packet : \n", peth->ether_type); + return OUT_DISCARD; + } + + return 1; +} + +static struct task_init task_init_lb_net = { + .mode_str = "lbnetwork", + .init = init_task_lb_net, + .handle = handle_lb_net_bulk, + .size = sizeof(struct task_lb_net), + .flag_features = TASK_FEATURE_GRE_ID +}; + +static struct task_init task_init_lb_net_lut_qinq_rss = { + .mode_str = "lbnetwork", + .sub_mode_str = "lut_qinq_rss", + .init = init_task_lb_net_lut, + .handle = handle_lb_net_lut_bulk, + .size = sizeof(struct task_lb_net_lut), + .flag_features = TASK_FEATURE_LUT_QINQ_RSS +}; + +static struct task_init task_init_lb_net_lut_qinq_hash = { + .mode_str = "lbnetwork", + .sub_mode_str = "lut_qinq_hash", + .init = init_task_lb_net_lut, + .handle = handle_lb_net_lut_bulk, + .size = sizeof(struct task_lb_net_lut), + .flag_features = TASK_FEATURE_LUT_QINQ_HASH +}; + +static struct task_init task_init_lb_net_indexed_table_rss = { + .mode_str = "lbnetwork", + .sub_mode_str = "indexed_table_rss", + .init = init_task_lb_net_indexed_table, + .handle = handle_lb_net_indexed_table_bulk, + .size = sizeof(struct task_lb_net_lut), + .flag_features = TASK_FEATURE_LUT_QINQ_RSS +}; + +static struct task_init task_init_lb_net_indexed_table_hash = { + .mode_str = "lbnetwork", + .sub_mode_str = "indexed_table_hash", + .init = init_task_lb_net_indexed_table, + .handle = handle_lb_net_indexed_table_bulk, + .size = sizeof(struct task_lb_net_lut), + .flag_features = TASK_FEATURE_LUT_QINQ_HASH +}; + +__attribute__((constructor)) static void reg_task_lb_net(void) +{ + reg_task(&task_init_lb_net); + reg_task(&task_init_lb_net_lut_qinq_rss); + reg_task(&task_init_lb_net_lut_qinq_hash); + reg_task(&task_init_lb_net_indexed_table_rss); + reg_task(&task_init_lb_net_indexed_table_hash); +} diff --git a/VNFs/DPPD-PROX/handle_lb_net.h b/VNFs/DPPD-PROX/handle_lb_net.h new file mode 100644 index 00000000..4124fbd6 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_net.h @@ -0,0 +1,27 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_LB_NET_H_ +#define _HANDLE_LB_NET_H_ + +#include "defaults.h" + +static inline int8_t rss_to_queue(int rss, int nb_queues) +{ + return (rss & ((1 << MAX_RSS_QUEUE_BITS) - 1)) % nb_queues; +} + +#endif /* _HANDLE_LB_NET_H_ */ diff --git a/VNFs/DPPD-PROX/handle_lb_pos.c b/VNFs/DPPD-PROX/handle_lb_pos.c new file mode 100644 index 00000000..4324e94d --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_pos.c @@ -0,0 +1,156 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_hash_crc.h> + +#include "log.h" +#include "task_base.h" +#include "defines.h" +#include "tx_pkt.h" +#include "task_init.h" +#include "quit.h" +#include "mpls.h" +#include "etypes.h" +#include "gre.h" +#include "prefetch.h" + +struct task_lb_pos { + struct task_base base; + uint16_t byte_offset; + uint8_t n_workers; +}; + +static void init_task_lb_pos(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_pos *task = (struct task_lb_pos *)tbase; + + task->n_workers = targ->nb_worker_threads; + task->byte_offset = targ->byte_offset; +} + +static int handle_lb_pos_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_pos *task = (struct task_lb_pos *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t offset = task->byte_offset; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + uint8_t* pkt = rte_pktmbuf_mtod(mbufs[j], uint8_t*); + out[j] = pkt[offset] % task->n_workers; + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + uint8_t* pkt = rte_pktmbuf_mtod(mbufs[j], uint8_t*); + out[j] = pkt[offset] % task->n_workers; + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +union ip_port { + struct { + uint32_t ip; + uint32_t port; + }; + uint64_t ip_port; +}; + +struct pkt_ether_ipv4_udp { + struct ether_hdr ether; + struct ipv4_hdr ipv4; + struct udp_hdr udp; +} __attribute__((unused)); + +static uint8_t handle_lb_ip_port(struct task_lb_pos *task, struct rte_mbuf *mbuf) +{ + union ip_port ip_port; + uint8_t ret; + + struct pkt_ether_ipv4_udp *pkt = rte_pktmbuf_mtod(mbuf, void *); + + if (pkt->ether.ether_type != ETYPE_IPv4 || + (pkt->ipv4.next_proto_id != IPPROTO_TCP && + pkt->ipv4.next_proto_id != IPPROTO_UDP)) + return OUT_DISCARD; + + if (task->byte_offset == 0) { + ip_port.ip = pkt->ipv4.src_addr; + ip_port.port = pkt->udp.src_port; + } + else { + ip_port.ip = pkt->ipv4.dst_addr; + ip_port.port = pkt->udp.dst_port; + } + + return rte_hash_crc(&ip_port.ip_port, sizeof(ip_port.ip_port), 0) % task->n_workers; +} + +static int handle_lb_ip_port_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_pos *task = (struct task_lb_pos *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + uint64_t ip_port = 0; + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_lb_ip_port(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_lb_ip_port(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static struct task_init task_init_lb_pos = { + .mode_str = "lbpos", + .init = init_task_lb_pos, + .handle = handle_lb_pos_bulk, + .size = sizeof(struct task_lb_pos) +}; + +static struct task_init task_init_lb_pos2 = { + .mode_str = "lbpos", + .sub_mode_str = "ip_port", + .init = init_task_lb_pos, + .handle = handle_lb_ip_port_bulk, + .size = sizeof(struct task_lb_pos) +}; + +__attribute__((constructor)) static void reg_task_lb_pos(void) +{ + reg_task(&task_init_lb_pos); + reg_task(&task_init_lb_pos2); +} diff --git a/VNFs/DPPD-PROX/handle_lb_qinq.c b/VNFs/DPPD-PROX/handle_lb_qinq.c new file mode 100644 index 00000000..d58703c5 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_lb_qinq.c @@ -0,0 +1,377 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> + +#include <rte_mbuf.h> +#include <rte_ip.h> +#include <rte_byteorder.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "task_base.h" +#include "tx_pkt.h" +#include "rx_pkt.h" +#include "etypes.h" +#include "log.h" +#include "quit.h" +#include "qinq.h" +#include "lconf.h" +#include "prefetch.h" +#include "defines.h" +#include "prox_cfg.h" +#include "hash_utils.h" +#include "handle_lb_net.h" +#include "toeplitz.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +/* Load balancing based on one byte, figures out what type of packet + is passed and depending on the type, pass the packet to the correct + worker thread. If an unsupported packet type is used, the packet is + simply dropped. This Load balancer can only handling QinQ packets + (i.e. packets comming from the vCPE). */ +int handle_lb_qinq_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +int handle_lb_qinq_bulk_set_port(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + +struct task_lb_qinq { + struct task_base base; + uint8_t *worker_table; + uint8_t bit_mask; + uint8_t protocols_mask; + uint8_t nb_worker_threads; + uint16_t qinq_tag; +}; + +static void init_task_lb_qinq(struct task_base *tbase, struct task_args *targ) +{ + struct task_lb_qinq *task = (struct task_lb_qinq *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->qinq_tag = targ->qinq_tag; + task->nb_worker_threads = targ->nb_worker_threads; + task->bit_mask = rte_is_power_of_2(targ->nb_worker_threads) ? targ->nb_worker_threads - 1 : 0xff; + + /* The load distributor is sending to a set of cores. These + cores are responsible for handling a set of flows + identified by a qinq tag. The load distributor identifies + the flows and forwards them to the appropriate worker. The + mapping from flow to worker is stored within the + work_table. Build the worker_table by asking each worker + which flows are handled. */ + + task->worker_table = prox_zmalloc(0x1000000, socket_id); + for (int i = 0; i < targ->nb_worker_threads; ++i) { + struct core_task ct = targ->core_task_set[0].core_task[i]; + struct task_args *t = core_targ_get(ct.core, ct.task); + + PROX_PANIC(t->task_init->flow_iter.beg == NULL, + "Load distributor can't find flows owned by destination worker %d\n", i); + + struct flow_iter *it = &t->task_init->flow_iter; + + int cnt = 0; + for (it->beg(it, t); !it->is_end(it, t); it->next(it, t)) { + uint16_t svlan = it->get_svlan(it, t); + uint16_t cvlan = it->get_cvlan(it, t); + + task->worker_table[PKT_TO_LUTQINQ(svlan, cvlan)] = i; + } + + } + + /* Check which protocols we are allowed to send to worker tasks */ + for (int i = 0; i < MAX_PROTOCOLS; ++i) { + int is_active = !!targ->core_task_set[i].n_elems; + task->protocols_mask |= is_active << i; + } + plog_info("\t\ttask_lb_qinq protocols_mask = 0x%x\n", task->protocols_mask); + + if (targ->task_init->flag_features & TASK_FEATURE_LUT_QINQ_RSS) + tbase->flags |= BASE_FLAG_LUT_QINQ_RSS; + if (targ->task_init->flag_features & TASK_FEATURE_LUT_QINQ_HASH) + tbase->flags |= BASE_FLAG_LUT_QINQ_HASH; + plog_info("\t\ttask_lb_qinq flags = 0x%x\n", tbase->flags); +} + +static struct task_init task_init_lb_qinq = { + .mode_str = "lbqinq", + .init = init_task_lb_qinq, + .handle = handle_lb_qinq_bulk, + .size = sizeof(struct task_lb_qinq) +}; + +/* + Add correct port id to mbufs coming from a DPDK ring port in the loadbalancer. + For the split-bng using DPDK rings between the vSwitch and the VMs + we need to know the port from which a packet was received. + The ring PMD in dpdk does not update the port field in the mbuf + and thus we have no control over the port numbers that are being used. + This submode allows the loadbalancer to set the port number on which it + received the mbuf. +*/ +static struct task_init task_init_lb_qinq_set_port = { + .mode_str = "lbqinq", + .sub_mode_str = "lut_qinq_set_port", + .init = init_task_lb_qinq, + .handle = handle_lb_qinq_bulk_set_port, + .size = sizeof(struct task_lb_qinq) +}; + +/* + Load Balance on Hash of combination of cvlan and svlan +*/ +static struct task_init task_init_lb_qinq_hash_friend = { + .mode_str = "lbqinq", + .sub_mode_str ="lut_qinq_hash_friend", + .init = init_task_lb_qinq, + .handle = handle_lb_qinq_bulk, + .flag_features = TASK_FEATURE_LUT_QINQ_HASH, + .size = sizeof(struct task_lb_qinq) +}; + +/* + Load Balance on rss of combination of cvlan and svlan. + This could be used to compare with HW implementations. +*/ +static struct task_init task_init_lb_qinq_rss_friend = { + .mode_str = "lbqinq", + .sub_mode_str ="lut_qinq_rss_friend", + .init = init_task_lb_qinq, + .handle = handle_lb_qinq_bulk, + .flag_features = TASK_FEATURE_LUT_QINQ_RSS, + .size = sizeof(struct task_lb_qinq) +}; + +__attribute__((constructor)) static void reg_task_lb_qinq(void) +{ + reg_task(&task_init_lb_qinq); + reg_task(&task_init_lb_qinq_hash_friend); + reg_task(&task_init_lb_qinq_rss_friend); + reg_task(&task_init_lb_qinq_set_port); +} + +static inline uint8_t handle_lb_qinq(struct task_lb_qinq *task, struct rte_mbuf *mbuf); + +int handle_lb_qinq_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_qinq *task = (struct task_lb_qinq *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_lb_qinq(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_lb_qinq(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +int handle_lb_qinq_bulk_set_port(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_lb_qinq *task = (struct task_lb_qinq *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) + uint32_t port_id = mbufs[0]->pkt.in_port; +#else + uint32_t port_id = mbufs[0]->port; +#endif + + if (tbase->rx_pkt == rx_pkt_hw) { + port_id = tbase->rx_params_hw.last_read_portid + tbase->rx_params_hw.nb_rxports; + port_id = ( port_id - 1 ) % tbase->rx_params_hw.nb_rxports; + port_id = tbase->rx_params_hw.rx_pq[port_id].port; + } else if (tbase->rx_pkt == rx_pkt_hw1) { + port_id = tbase->rx_params_hw1.rx_pq.port; + } + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) + mbufs[j]->pkt.in_port = port_id; +#else + mbufs[j]->port = port_id; +#endif + out[j] = handle_lb_qinq(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) + mbufs[j]->pkt.in_port = port_id; +#else + mbufs[j]->port = port_id; +#endif + out[j] = handle_lb_qinq(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +struct qinq_packet { + struct qinq_hdr qinq_hdr; + union { + struct ipv4_hdr ipv4_hdr; + struct ipv6_hdr ipv6_hdr; + }; +} __attribute__((packed)); + +struct qinq_packet_data { + struct ether_addr d_addr; + struct ether_addr s_addr; + uint64_t qinq; +} __attribute__((packed)); + +struct ether_packet { + struct ether_hdr ether_hdr; + union { + struct ipv4_hdr ipv4_hdr; + struct ipv6_hdr ipv6_hdr; + }; +} __attribute__((packed)); + +struct cpe_packet { + union { + struct qinq_packet qp; + struct ether_packet ep; + struct qinq_packet_data qd; + }; +}; + +static inline uint8_t get_worker(struct task_lb_qinq *task, struct cpe_packet *packet) +{ + uint8_t worker = 0; + if (((struct task_base *)task)->flags & BASE_FLAG_LUT_QINQ_HASH) { + // Load Balance on Hash of combination of cvlan and svlan + uint64_t qinq_net = packet->qd.qinq; + qinq_net = qinq_net & 0xFF0F0000FF0F0000; // Mask Proto and QoS bits + if (task->bit_mask != 0xff) { + worker = hash_crc32(&qinq_net,8,0) & task->bit_mask; + } + else { + worker = hash_crc32(&qinq_net,8,0) % task->nb_worker_threads; + } + plogx_dbg("Sending packet svlan=%x, cvlan=%x, pseudo_qinq=%lx to worker %d\n", rte_bswap16(0xFF0F & packet->qp.qinq_hdr.svlan.vlan_tci), rte_bswap16(0xFF0F & packet->qp.qinq_hdr.cvlan.vlan_tci), qinq_net, worker); + } else if (((struct task_base *)task)->flags & BASE_FLAG_LUT_QINQ_RSS){ + // Load Balance on rss of combination of cvlan and svlan + uint32_t qinq = (packet->qp.qinq_hdr.cvlan.vlan_tci & 0xFF0F) << 16; + uint32_t rss = toeplitz_hash((uint8_t *)&qinq, 4); + if (task->bit_mask != 0xff) { + worker = rss & task->bit_mask; + } else { + worker = (0x1ff & rss) % task->nb_worker_threads; + } + plogx_dbg("Sending packet svlan=%x, cvlan=%x, rss_input=%x, rss=%x to worker %d\n", rte_bswap16(0xFF0F & packet->qp.qinq_hdr.svlan.vlan_tci), rte_bswap16(0xFF0F & packet->qp.qinq_hdr.cvlan.vlan_tci), qinq, rss, worker); + } else { + uint16_t svlan = packet->qp.qinq_hdr.svlan.vlan_tci; + uint16_t cvlan = packet->qp.qinq_hdr.cvlan.vlan_tci; + prefetch_nta(&task->worker_table[PKT_TO_LUTQINQ(svlan, cvlan)]); + worker = task->worker_table[PKT_TO_LUTQINQ(svlan, cvlan)]; + + const size_t pos = offsetof(struct cpe_packet, qp.qinq_hdr.cvlan.vlan_tci); + plogx_dbg("qinq = %u, worker = %u, pos = %lu\n", rte_be_to_cpu_16(cvlan), worker, pos); + } + return worker; +} + +static inline uint8_t handle_lb_qinq(struct task_lb_qinq *task, struct rte_mbuf *mbuf) +{ + struct cpe_packet *packet = rte_pktmbuf_mtod(mbuf, struct cpe_packet*); + if (packet->ep.ether_hdr.ether_type == ETYPE_IPv4) { + if (unlikely((packet->ep.ipv4_hdr.version_ihl >> 4) != 4)) { + plogx_err("Invalid Version %u for ETYPE_IPv4\n", packet->ep.ipv4_hdr.version_ihl); + return OUT_DISCARD; + } + /* use 24 bits from the IP, clients are from the 10.0.0.0/8 network */ + const uint32_t tmp = rte_bswap32(packet->ep.ipv4_hdr.src_addr) & 0x00FFFFFF; + const uint32_t svlan = rte_bswap16(tmp >> 12); + const uint32_t cvlan = rte_bswap16(tmp & 0x0FFF); + prefetch_nta(&task->worker_table[PKT_TO_LUTQINQ(svlan, cvlan)]); + uint8_t worker = task->worker_table[PKT_TO_LUTQINQ(svlan, cvlan)]; + return worker + IPV4 * task->nb_worker_threads; + } + else if (unlikely(packet->qp.qinq_hdr.svlan.eth_proto != task->qinq_tag)) { + /* might receive LLDP from the L2 switch... */ + if (packet->qp.qinq_hdr.svlan.eth_proto != ETYPE_LLDP) { + plogdx_err(mbuf, "Invalid packet for LB in QinQ mode\n"); + } + return OUT_DISCARD; + } + + uint8_t worker = 0; + uint8_t proto = 0xFF; + switch (packet->qp.qinq_hdr.ether_type) { + case ETYPE_IPv4: { + if (unlikely((packet->qp.ipv4_hdr.version_ihl >> 4) != 4)) { + plogx_err("Invalid Version %u for ETYPE_IPv4\n", packet->qp.ipv4_hdr.version_ihl); + return OUT_DISCARD; + } + worker = get_worker(task, packet); + proto = IPV4; + break; + } + case ETYPE_IPv6: { + if (unlikely((packet->qp.ipv4_hdr.version_ihl >> 4) != 6)) { + plogx_err("Invalid Version %u for ETYPE_IPv6\n", packet->qp.ipv4_hdr.version_ihl); + return OUT_DISCARD; + } + /* Use IP Destination when IPV6 QinQ */ + if (task->bit_mask != 0xff) { + worker = ((uint8_t *)packet)[61] & task->bit_mask; + } + else { + worker = ((uint8_t *)packet)[61] % task->nb_worker_threads; + } + proto = IPV6; + break; + } + case ETYPE_ARP: { + // We can only send to ARP ring if it exists + if (0 != (task->protocols_mask & (1 << ARP))) { + proto = ARP; + } else { + proto = IPV4; + } + worker = get_worker(task, packet); + break; + } + default: + plogx_warn("Error in ETYPE_8021ad: ether_type = %#06x\n", packet->qp.qinq_hdr.ether_type); + return OUT_DISCARD; + } + + return worker + proto * task->nb_worker_threads; +} diff --git a/VNFs/DPPD-PROX/handle_mirror.c b/VNFs/DPPD-PROX/handle_mirror.c new file mode 100644 index 00000000..0d764b4d --- /dev/null +++ b/VNFs/DPPD-PROX/handle_mirror.c @@ -0,0 +1,161 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <rte_mbuf.h> + +#include "mbuf_utils.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "prox_port_cfg.h" +#include "quit.h" + +/* Task that sends packets to multiple outputs. Note that in case of n + outputs, the output packet rate is n times the input packet + rate. Also, since the packet is duplicated by increasing the + refcnt, a change to a packet in subsequent tasks connected through + one of the outputs of this task will also change the packets as + seen by tasks connected behind through other outputs. The correct + way to resolve this is to create deep copies of the packet. */ +struct task_mirror { + struct task_base base; + uint32_t n_dests; +}; + +struct task_mirror_copy { + struct task_base base; + struct rte_mempool *mempool; + uint32_t n_dests; +}; + +static int handle_mirror_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + int ret = 0; + struct task_mirror *task = (struct task_mirror *)tbase; + uint8_t out[MAX_PKT_BURST]; + struct rte_mbuf *mbufs2[MAX_PKT_BURST]; + + /* Since after calling tx_pkt the mbufs parameter of a handle + function becomes invalid and handle_mirror calls tx_pkt + multiple times, the pointers are copied first. This copy is + used in each call to tx_pkt below. */ + rte_memcpy(mbufs2, mbufs, sizeof(mbufs[0]) * n_pkts); + + for (uint16_t j = 0; j < n_pkts; ++j) { + rte_pktmbuf_refcnt_update(mbufs2[j], task->n_dests - 1); + } + for (uint16_t j = 0; j < task->n_dests; ++j) { + memset(out, j, n_pkts); + + ret+= task->base.tx_pkt(&task->base, mbufs2, n_pkts, out); + } + return ret; +} + +static int handle_mirror_bulk_copy(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_mirror_copy *task = (struct task_mirror_copy *)tbase; + uint8_t out[MAX_PKT_BURST]; + int ret = 0; + + /* Send copies of the packet to all but the first + destination */ + struct rte_mbuf *new_pkts[MAX_PKT_BURST]; + + for (uint16_t j = 1; j < task->n_dests; ++j) { + if (rte_mempool_get_bulk(task->mempool, (void **)new_pkts, n_pkts) < 0) { + continue; + } + /* Finally, forward the incoming packets. */ + for (uint16_t i = 0; i < n_pkts; ++i) { + void *dst, *src; + uint16_t pkt_len; + + out[i] = j; + init_mbuf_seg(new_pkts[i]); + + pkt_len = rte_pktmbuf_pkt_len(mbufs[i]); + rte_pktmbuf_pkt_len(new_pkts[i]) = pkt_len; + rte_pktmbuf_data_len(new_pkts[i]) = pkt_len; + + dst = rte_pktmbuf_mtod(new_pkts[i], void *); + src = rte_pktmbuf_mtod(mbufs[i], void *); + + rte_memcpy(dst, src, pkt_len); + } + ret+= task->base.tx_pkt(&task->base, new_pkts, n_pkts, out); + } + + /* Finally, forward the incoming packets to the first destination. */ + memset(out, 0, n_pkts); + ret+= task->base.tx_pkt(&task->base, mbufs, n_pkts, out); + return ret; +} + +static void init_task_mirror(struct task_base *tbase, struct task_args *targ) +{ + struct task_mirror *task = (struct task_mirror *)tbase; + + task->n_dests = targ->nb_txports? targ->nb_txports : targ->nb_txrings; +} + +static void init_task_mirror_copy(struct task_base *tbase, struct task_args *targ) +{ + static char name[] = "mirror_pool"; + struct task_mirror_copy *task = (struct task_mirror_copy *)tbase; + const int sock_id = rte_lcore_to_socket_id(targ->lconf->id); + task->n_dests = targ->nb_txports? targ->nb_txports : targ->nb_txrings; + + name[0]++; + task->mempool = rte_mempool_create(name, + targ->nb_mbuf - 1, MBUF_SIZE, + targ->nb_cache_mbuf, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + rte_pktmbuf_init, 0, + sock_id, 0); + PROX_PANIC(task->mempool == NULL, + "Failed to allocate memory pool on socket %u with %u elements\n", + sock_id, targ->nb_mbuf - 1); + task->n_dests = targ->nb_txports? targ->nb_txports : targ->nb_txrings; +} + +static struct task_init task_init_mirror = { + .mode_str = "mirror", + .init = init_task_mirror, + .handle = handle_mirror_bulk, + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS | TASK_FEATURE_TXQ_FLAGS_REFCOUNT, + .size = sizeof(struct task_mirror), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +static struct task_init task_init_mirror2 = { + .mode_str = "mirror", + .sub_mode_str = "copy", + .init = init_task_mirror_copy, + .handle = handle_mirror_bulk_copy, + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS | TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .size = sizeof(struct task_mirror), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_mirror(void) +{ + reg_task(&task_init_mirror); + reg_task(&task_init_mirror2); +} diff --git a/VNFs/DPPD-PROX/handle_mplstag.c b/VNFs/DPPD-PROX/handle_mplstag.c new file mode 100644 index 00000000..ce5996eb --- /dev/null +++ b/VNFs/DPPD-PROX/handle_mplstag.c @@ -0,0 +1,157 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "defines.h" +#include "hash_entry_types.h" +#include "mpls.h" +#include "prefetch.h" +#include "task_base.h" +#include "tx_pkt.h" +#include "task_init.h" +#include "prox_port_cfg.h" +#include "prox_cksum.h" +#include "thread_generic.h" +#include "prefetch.h" +#include "prox_assert.h" +#include "etypes.h" +#include "log.h" +#include "mbuf_utils.h" + +struct task_unmpls { + struct task_base base; + uint8_t n_tags; +}; + +static void init_task_unmpls(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ +} + +static inline uint8_t handle_unmpls(__attribute__((unused)) struct task_unmpls *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct mpls_hdr *mpls = (struct mpls_hdr *)(peth + 1); + uint32_t mpls_len = sizeof(struct mpls_hdr); + while (!(mpls->bytes & 0x00010000)) { + mpls++; + mpls_len += sizeof(struct mpls_hdr); + } + uint32_t tot_eth_addr_len = 2*sizeof(struct ether_addr); + rte_memcpy(((uint8_t *)peth) + mpls_len, peth, tot_eth_addr_len); + struct ipv4_hdr *ip = (struct ipv4_hdr *)(mpls + 1); + switch (ip->version_ihl >> 4) { + case 4: + peth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, mpls_len); + peth->ether_type = ETYPE_IPv4; + return 0; + case 6: + peth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, mpls_len); + peth->ether_type = ETYPE_IPv6; + return 0; + default: + plog_warn("Failed Decoding MPLS Packet - neither IPv4 nor IPv6: version %u\n", ip->version_ihl); + return OUT_DISCARD; + } +} + +static int handle_unmpls_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_unmpls *task = (struct task_unmpls *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + prefetch_first(mbufs, n_pkts); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_unmpls(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_unmpls(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static struct task_init task_init_unmpls = { + .mode_str = "unmpls", + .init = init_task_unmpls, + .handle = handle_unmpls_bulk, + .thread_x = thread_generic, + .size = sizeof(struct task_unmpls) +}; + +struct task_tagmpls { + struct task_base base; + uint8_t n_tags; +}; + +static void init_task_tagmpls(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ +} + +static inline uint8_t handle_tagmpls(__attribute__((unused)) struct task_tagmpls *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, 4); + PROX_ASSERT(peth); + rte_prefetch0(peth); + uint32_t mpls = 0; + + uint32_t tot_eth_addr_len = 2*sizeof(struct ether_addr); + rte_memcpy(peth, ((uint8_t *)peth) + sizeof(struct mpls_hdr), tot_eth_addr_len); + *((uint32_t *)(peth + 1)) = mpls | 0x00010000; // Set BoS to 1 + peth->ether_type = ETYPE_MPLSU; + return 0; +} + +static int handle_tagmpls_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_tagmpls *task = (struct task_tagmpls *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + prefetch_first(mbufs, n_pkts); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_tagmpls(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_tagmpls(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static struct task_init task_init_tagmpls = { + .mode_str = "tagmpls", + .init = init_task_tagmpls, + .handle = handle_tagmpls_bulk, + .size = sizeof(struct task_tagmpls) +}; + +__attribute__((constructor)) static void reg_task_mplstag(void) +{ + reg_task(&task_init_unmpls); + reg_task(&task_init_tagmpls); +} diff --git a/VNFs/DPPD-PROX/handle_nat.c b/VNFs/DPPD-PROX/handle_nat.c new file mode 100644 index 00000000..23d7ad87 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_nat.c @@ -0,0 +1,196 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_hash.h> +#include <rte_hash_crc.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_version.h> +#include <rte_byteorder.h> + +#include "prox_lua_types.h" +#include "prox_lua.h" +#include "prox_malloc.h" +#include "prox_cksum.h" +#include "prefetch.h" +#include "etypes.h" +#include "log.h" +#include "quit.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "prox_port_cfg.h" + +struct task_nat { + struct task_base base; + struct rte_hash *hash; + uint32_t *entries; + int use_src; + int offload_crc; +}; + +struct pkt_eth_ipv4 { + struct ether_hdr ether_hdr; + struct ipv4_hdr ipv4_hdr; +} __attribute__((packed)); + +static int handle_nat(struct task_nat *task, struct rte_mbuf *mbuf) +{ + uint32_t *ip_addr; + struct pkt_eth_ipv4 *pkt = rte_pktmbuf_mtod(mbuf, struct pkt_eth_ipv4 *); + int ret; + + /* Currently, only support eth/ipv4 packets */ + if (pkt->ether_hdr.ether_type != ETYPE_IPv4) + return OUT_DISCARD; + if (task->use_src) + ip_addr = &(pkt->ipv4_hdr.src_addr); + else + ip_addr = &(pkt->ipv4_hdr.dst_addr); + + ret = rte_hash_lookup(task->hash, ip_addr); + + /* Drop all packets for which no translation has been + configured. */ + if (ret < 0) + return OUT_DISCARD; + + *ip_addr = task->entries[ret]; + prox_ip_udp_cksum(mbuf, &pkt->ipv4_hdr, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + return 0; +} + +static int handle_nat_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_nat *task = (struct task_nat *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + prefetch_first(mbufs, n_pkts); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_nat(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_nat(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int lua_to_hash_nat(struct lua_State *L, enum lua_place from, const char *name, + uint8_t socket, struct rte_hash **hash, uint32_t **entries) +{ + struct rte_hash *ret_hash; + uint32_t *ret_entries; + uint32_t n_entries; + uint32_t ip_from, ip_to; + int ret, pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + PROX_PANIC(n_entries == 0, "No entries for NAT\n"); + + static char hash_name[30] = "000_hash_nat_table"; + + const struct rte_hash_parameters hash_params = { + .name = hash_name, + .entries = n_entries * 4, + .key_len = sizeof(ip_from), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + }; + + ret_hash = rte_hash_create(&hash_params); + PROX_PANIC(ret_hash == NULL, "Failed to set up hash table for NAT\n"); + name++; + ret_entries = prox_zmalloc(n_entries * sizeof(ip_to), socket); + PROX_PANIC(ret_entries == NULL, "Failed to allocate memory for NAT %u entries\n", n_entries); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_ip(L, TABLE, "from", &ip_from) || + lua_to_ip(L, TABLE, "to", &ip_to)) + return -1; + + ip_from = rte_bswap32(ip_from); + ip_to = rte_bswap32(ip_to); + + ret = rte_hash_lookup(ret_hash, (const void *)&ip_from); + PROX_PANIC(ret >= 0, "Key %x already exists in NAT hash table\n", ip_from); + + ret = rte_hash_add_key(ret_hash, (const void *)&ip_from); + + PROX_PANIC(ret < 0, "Failed to add Key %x to NAT hash table\n", ip_from); + ret_entries[ret] = ip_to; + lua_pop(L, 1); + } + + lua_pop(L, pop); + + *hash = ret_hash; + *entries = ret_entries; + return 0; +} + +static void init_task_nat(struct task_base *tbase, struct task_args *targ) +{ + struct task_nat *task = (struct task_nat *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + int ret; + + /* Use destination IP by default. */ + task->use_src = targ->use_src; + + PROX_PANIC(!strcmp(targ->nat_table, ""), "No nat table specified\n"); + ret = lua_to_hash_nat(prox_lua(), GLOBAL, targ->nat_table, socket_id, &task->hash, &task->entries); + PROX_PANIC(ret != 0, "Failed to load NAT table from lua:\n%s\n", get_lua_to_errors()); + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } + +} + +/* Basic static nat. */ +static struct task_init task_init_nat = { + .mode_str = "nat", + .init = init_task_nat, + .handle = handle_nat_bulk, +#ifdef SOFT_CRC + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, +#else + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, +#endif + .size = sizeof(struct task_nat), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_nat(void) +{ + reg_task(&task_init_nat); +} diff --git a/VNFs/DPPD-PROX/handle_nop.c b/VNFs/DPPD-PROX/handle_nop.c new file mode 100644 index 00000000..b3eef54c --- /dev/null +++ b/VNFs/DPPD-PROX/handle_nop.c @@ -0,0 +1,53 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "handle_nop.h" +#include "thread_nop.h" + +static struct task_init task_init_nop_thrpt_opt = { + .mode_str = "nop", + .init = NULL, + .handle = handle_nop_bulk, + .thread_x = thread_nop, + .flag_features = TASK_FEATURE_NEVER_DISCARDS|TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_THROUGHPUT_OPT|TASK_FEATURE_MULTI_RX, + .size = sizeof(struct task_nop), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +static struct task_init task_init_nop_lat_opt = { + .mode_str = "nop", + .sub_mode_str = "latency optimized", + .init = NULL, + .handle = handle_nop_bulk, + .thread_x = thread_nop, + .flag_features = TASK_FEATURE_NEVER_DISCARDS|TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_MULTI_RX, + .size = sizeof(struct task_nop), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +static struct task_init task_init_none; + +__attribute__((constructor)) static void reg_task_nop(void) +{ + reg_task(&task_init_nop_thrpt_opt); + reg_task(&task_init_nop_lat_opt); + + /* For backwards compatibility, add none */ + task_init_none = task_init_nop_thrpt_opt; + strcpy(task_init_none.mode_str, "none"); + + reg_task(&task_init_none); +} diff --git a/VNFs/DPPD-PROX/handle_nop.h b/VNFs/DPPD-PROX/handle_nop.h new file mode 100644 index 00000000..0f84eaa3 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_nop.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_NOP_H_ +#define _HANDLE_NOP_H_ + +#include "task_base.h" +#include "task_init.h" + +struct task_nop { + struct task_base base; +}; + +static inline int handle_nop_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_nop *task = (struct task_nop *)tbase; + return task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); +} + +#endif /* _HANDLE_NOP_H_ */ diff --git a/VNFs/DPPD-PROX/handle_nsh.c b/VNFs/DPPD-PROX/handle_nsh.c new file mode 100644 index 00000000..65a80c3d --- /dev/null +++ b/VNFs/DPPD-PROX/handle_nsh.c @@ -0,0 +1,225 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ethdev.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> + +#include "vxlangpe_nsh.h" +#include "task_base.h" +#include "tx_pkt.h" +#include "task_init.h" +#include "thread_generic.h" +#include "prefetch.h" +#include "log.h" + +#define VXLAN_GPE_HDR_SZ sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr) + sizeof(struct vxlan_gpe_hdr) + sizeof(struct nsh_hdr) +#define ETHER_NSH_TYPE 0x4F89 /* 0x894F in little endian */ +#define VXLAN_GPE_NSH_TYPE 0xB612 /* 4790 in little endian */ +#define VXLAN_GPE_NP 0x4 + +uint16_t decap_nsh_packets(struct rte_mbuf **mbufs, uint16_t n_pkts); +uint16_t encap_nsh_packets(struct rte_mbuf **mbufs, uint16_t n_pkts); + +struct task_decap_nsh { + struct task_base base; +}; + +struct task_encap_nsh { + struct task_base base; +}; + +static void init_task_decap_nsh(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ + return; +} + +static inline uint8_t handle_decap_nsh(__attribute__((unused)) struct task_decap_nsh *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *eth_hdr = NULL; + struct udp_hdr *udp_hdr = NULL; + struct vxlan_gpe_hdr *vxlan_gpe_hdr = NULL; + uint16_t hdr_len; + + eth_hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + if (eth_hdr->ether_type == ETHER_NSH_TYPE) { + /* "decapsulate" Ethernet + NSH header by moving packet pointer */ + hdr_len = sizeof(struct ether_hdr) + sizeof(struct nsh_hdr); + + mbuf->data_len = (uint16_t)(mbuf->data_len - hdr_len); + mbuf->data_off += hdr_len; + mbuf->pkt_len = (uint32_t)(mbuf->pkt_len - hdr_len); + /* save length of header in reserved 16bits of rte_mbuf */ + mbuf->udata64 = hdr_len; + } + else { + if (mbuf->data_len < VXLAN_GPE_HDR_SZ) { + mbuf->udata64 = 0; + return 0; + } + + /* check the UDP destination port */ + udp_hdr = (struct udp_hdr *)(((unsigned char *)eth_hdr) + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr)); + if (udp_hdr->dst_port != VXLAN_GPE_NSH_TYPE) { + mbuf->udata64 = 0; + return 0; + } + + /* check the Next Protocol field in VxLAN-GPE header */ + vxlan_gpe_hdr = (struct vxlan_gpe_hdr *)(((unsigned char *)eth_hdr) + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); + if (vxlan_gpe_hdr->next_proto != VXLAN_GPE_NP) { + mbuf->udata64 = 0; + return 0; + } + + /* "decapsulate" VxLAN-GPE + NSH header by moving packet pointer */ + hdr_len = VXLAN_GPE_HDR_SZ; + + mbuf->data_len = (uint16_t)(mbuf->data_len - hdr_len); + mbuf->data_off += hdr_len; + mbuf->pkt_len = (uint32_t)(mbuf->pkt_len - hdr_len); + /* save length of header in reserved 16bits of rte_mbuf */ + mbuf->udata64 = hdr_len; + } + + return 0; +} + +static int handle_decap_nsh_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_decap_nsh *task = (struct task_decap_nsh *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_decap_nsh(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_decap_nsh(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void init_task_encap_nsh(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ + return; +} + +static inline uint8_t handle_encap_nsh(__attribute__((unused)) struct task_encap_nsh *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *eth_hdr = NULL; + struct nsh_hdr *nsh_hdr = NULL; + struct udp_hdr *udp_hdr = NULL; + struct vxlan_gpe_hdr *vxlan_gpe_hdr = NULL; + uint16_t hdr_len; + + if (mbuf == NULL) + return 0; + if (mbuf->udata64 == 0) + return 0; + + /* use header length saved in reserved 16bits of rte_mbuf to + "encapsulate" transport + NSH header by moving packet pointer */ + mbuf->data_len = (uint16_t)(mbuf->data_len + mbuf->udata64); + mbuf->data_off -= mbuf->udata64; + mbuf->pkt_len = (uint32_t)(mbuf->pkt_len + mbuf->udata64); + + eth_hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + if (eth_hdr->ether_type == ETHER_NSH_TYPE) { + nsh_hdr = (struct nsh_hdr *) (((unsigned char *)eth_hdr) + sizeof(struct ether_hdr)); + + /* decrement Service Index in NSH header */ + if (nsh_hdr->sf_index > 0) + nsh_hdr->sf_index -= 1; + } + else { + /* "encapsulate" VxLAN-GPE + NSH header by moving packet pointer */ + if (mbuf->data_len < VXLAN_GPE_HDR_SZ) + return 0; + + /* check the UDP destination port */ + udp_hdr = (struct udp_hdr *)(((unsigned char *)eth_hdr) + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr)); + if (udp_hdr->dst_port != VXLAN_GPE_NSH_TYPE) + return 0; + + /* check the Next Protocol field in VxLAN-GPE header */ + vxlan_gpe_hdr = (struct vxlan_gpe_hdr *)(((unsigned char *)eth_hdr) + sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr)); + if (vxlan_gpe_hdr->next_proto != VXLAN_GPE_NP) + return 0; + + /* decrement Service Index in NSH header */ + nsh_hdr = (struct nsh_hdr *)(((unsigned char *)vxlan_gpe_hdr) + sizeof(struct vxlan_gpe_hdr)); + if (nsh_hdr->sf_index > 0) + nsh_hdr->sf_index -= 1; + } + + return 0; +} + +static int handle_encap_nsh_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_encap_nsh *task = (struct task_encap_nsh *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_encap_nsh(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_encap_nsh(task, mbufs[j]); + } +#endif + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static struct task_init task_init_decap_nsh = { + .mode_str = "decapnsh", + .init = init_task_decap_nsh, + .handle = handle_decap_nsh_bulk, + .thread_x = thread_generic, + .size = sizeof(struct task_decap_nsh) +}; + +static struct task_init task_init_encap_nsh = { + .mode_str = "encapnsh", + .init = init_task_encap_nsh, + .handle = handle_encap_nsh_bulk, + .size = sizeof(struct task_encap_nsh) +}; + +__attribute__((constructor)) static void reg_task_nshtag(void) +{ + reg_task(&task_init_decap_nsh); + reg_task(&task_init_encap_nsh); +} diff --git a/VNFs/DPPD-PROX/handle_pf_acl.c b/VNFs/DPPD-PROX/handle_pf_acl.c new file mode 100644 index 00000000..16ac0330 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_pf_acl.c @@ -0,0 +1,104 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_table_stub.h> //FIXME: ACL + +#include "log.h" +#include "quit.h" +#include "thread_pipeline.h" + +struct task_pf_acl { + struct task_pipe pipe; + //TODO +}; + +static void init_task_pf_acl(struct task_base *tbase, struct task_args *targ) +{ + struct task_pipe *tpipe = (struct task_pipe *)tbase; +// struct task_pf_acl *task = (struct task_pf_acl *)tpipe; + int err; + + /* create pipeline, input ports and output ports */ + init_pipe_create_in_out(tpipe, targ); + + /* create ACL pipeline table */ + //TODO + +//FIXME: this is not ACL ( + /* create pipeline tables */ + for (uint8_t i = 0; i < tpipe->n_ports_in; ++i) { + struct rte_pipeline_table_params table_params = { + .ops = &rte_table_stub_ops, + .arg_create = NULL, + .f_action_hit = NULL, + .f_action_miss = NULL, + .arg_ah = NULL, + .action_data_size = 0, + }; + err = rte_pipeline_table_create(tpipe->p, &table_params, + &tpipe->table_id[i]); + PROX_PANIC(err != 0, "Failed to create table %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, targ->task_init->mode_str, + targ->lconf->id, targ->task, + err); + } + tpipe->n_tables = tpipe->n_ports_in; + PROX_PANIC(tpipe->n_tables < 1, "No table created " + "for %s pipeline on core %u task %u\n", + targ->task_init->mode_str, targ->lconf->id, targ->task); + + /* add default entry to tables */ + for (uint8_t i = 0; i < tpipe->n_tables; ++i) { + struct rte_pipeline_table_entry default_entry = { + .action = RTE_PIPELINE_ACTION_PORT, + {.port_id = tpipe->port_out_id[i % tpipe->n_ports_out]}, + }; + struct rte_pipeline_table_entry *default_entry_ptr; + err = rte_pipeline_table_default_entry_add(tpipe->p, tpipe->table_id[i], + &default_entry, &default_entry_ptr); + PROX_PANIC(err != 0, "Failed to add default entry to table %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, targ->task_init->mode_str, + targ->lconf->id, targ->task, + err); + } +//FIXME: this is not ACL ) + + /* connect pipeline input ports to ACL pipeline table */ + init_pipe_connect_one(tpipe, targ, tpipe->table_id[0]); + + /* enable pipeline input ports */ + init_pipe_enable(tpipe, targ); + + /* check pipeline consistency */ + init_pipe_check(tpipe, targ); +} + +static struct task_init task_init_pf_acl = { + .mode_str = "pf_acl", + .init = init_task_pf_acl, + .handle = handle_pipe, + .thread_x = thread_pipeline, + .size = sizeof(struct task_pf_acl), +}; + +__attribute__((constructor)) static void reg_task_pf_acl(void) +{ + reg_task(&task_init_pf_acl); +} diff --git a/VNFs/DPPD-PROX/handle_police.c b/VNFs/DPPD-PROX/handle_police.c new file mode 100644 index 00000000..125e8c0a --- /dev/null +++ b/VNFs/DPPD-PROX/handle_police.c @@ -0,0 +1,270 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> +#include <rte_mbuf.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" +#include "prox_malloc.h" +#include "task_base.h" +#include "task_init.h" +#include "lconf.h" +#include "prefetch.h" +#include "quit.h" +#include "log.h" +#include "defines.h" +#include "qinq.h" +#include "prox_cfg.h" +#include "prox_shared.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +struct task_police { + struct task_base base; + union { + struct rte_meter_srtcm *sr_flows; + struct rte_meter_trtcm *tr_flows; + }; + + uint16_t *user_table; + enum police_action police_act[3][3]; + uint16_t overhead; + uint8_t runtime_flags; +}; + +typedef uint8_t (*hp) (struct task_police *task, struct rte_mbuf *mbuf, uint64_t tsc, uint32_t user); + +static uint8_t handle_police(struct task_police *task, struct rte_mbuf *mbuf, uint64_t tsc, uint32_t user) +{ + enum rte_meter_color in_color = e_RTE_METER_GREEN; + enum rte_meter_color out_color; + uint32_t pkt_len = rte_pktmbuf_pkt_len(mbuf) + task->overhead; + out_color = rte_meter_srtcm_color_aware_check(&task->sr_flows[user], tsc, pkt_len, in_color); + + return task->police_act[in_color][out_color] == ACT_DROP? OUT_DISCARD : 0; +} + +static uint8_t handle_police_tr(struct task_police *task, struct rte_mbuf *mbuf, uint64_t tsc, uint32_t user) +{ + enum rte_meter_color in_color = e_RTE_METER_GREEN; + enum rte_meter_color out_color; + uint32_t pkt_len = rte_pktmbuf_pkt_len(mbuf) + task->overhead; + out_color = rte_meter_trtcm_color_aware_check(&task->tr_flows[user], tsc, pkt_len, in_color); + + if (task->runtime_flags & TASK_MARK) { +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + uint32_t subport, pipe, traffic_class, queue; + enum rte_meter_color color; + + rte_sched_port_pkt_read_tree_path(mbuf, &subport, &pipe, &traffic_class, &queue); + color = task->police_act[in_color][out_color]; + + rte_sched_port_pkt_write(mbuf, subport, pipe, traffic_class, queue, color); +#else + struct rte_sched_port_hierarchy *sched = + (struct rte_sched_port_hierarchy *) &mbuf->pkt.hash.sched; + sched->color = task->police_act[in_color][out_color]; +#endif + } + + return task->police_act[in_color][out_color] == ACT_DROP? OUT_DISCARD : 0; +} + +static inline int get_user(struct task_police *task, struct rte_mbuf *mbuf) +{ + if (task->runtime_flags & TASK_CLASSIFY) { + struct qinq_hdr *pqinq = rte_pktmbuf_mtod(mbuf, struct qinq_hdr *); + return PKT_TO_LUTQINQ(pqinq->svlan.vlan_tci, pqinq->cvlan.vlan_tci); + } + +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + uint32_t dummy; + uint32_t pipe; + + rte_sched_port_pkt_read_tree_path(mbuf, &dummy, &pipe, &dummy, &dummy); + return pipe; +#else + struct rte_sched_port_hierarchy *sched = + (struct rte_sched_port_hierarchy *) &mbuf->pkt.hash.sched; + return sched->pipe; +#endif +} + +#define PHASE1_DELAY PREFETCH_OFFSET +#define PHASE2_DELAY PREFETCH_OFFSET +#define PHASE3_DELAY PREFETCH_OFFSET +#define PHASE4_DELAY PREFETCH_OFFSET + +static inline int handle_pb(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, hp handle_police_func) +{ + struct task_police *task = (struct task_police *)tbase; + uint16_t j; + uint64_t cur_tsc = rte_rdtsc(); + uint32_t user[64]; + uint8_t out[MAX_PKT_BURST]; + uint32_t cur_user; + for (j = 0; j < PHASE1_DELAY && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + + for (j = 0; j < PHASE2_DELAY && j + PHASE1_DELAY < n_pkts; ++j) { + PREFETCH0(mbufs[j + PHASE1_DELAY]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void*)); + } + + for (j = 0; j < PHASE3_DELAY && j + PHASE2_DELAY + PHASE1_DELAY < n_pkts; ++j) { + PREFETCH0(mbufs[j + PHASE2_DELAY + PHASE1_DELAY]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PHASE2_DELAY], void*)); + cur_user = get_user(task, mbufs[j]); + user[j] = cur_user; + PREFETCH0(&task->user_table[cur_user]); + } + + /* At this point, the whole pipeline is running */ + for (j = 0; j + PHASE3_DELAY + PHASE2_DELAY + PHASE1_DELAY < n_pkts; ++j) { + PREFETCH0(mbufs[j + PHASE3_DELAY + PHASE2_DELAY + PHASE1_DELAY]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PHASE3_DELAY + PHASE2_DELAY], void*)); + cur_user = get_user(task, mbufs[j + PHASE3_DELAY]); + user[j + PHASE3_DELAY] = cur_user; + PREFETCH0(&task->user_table[cur_user]); + + out[j] = handle_police_func(task, mbufs[j], cur_tsc, task->user_table[user[j]]); + } + + /* Last part of pipeline */ + for (; j + PHASE3_DELAY + PHASE2_DELAY < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PHASE3_DELAY + PHASE2_DELAY], void*)); + PREFETCH0(&task->user_table[j + PHASE3_DELAY]); + cur_user = get_user(task, mbufs[j + PHASE3_DELAY]); + user[j + PHASE3_DELAY] = cur_user; + PREFETCH0(&task->user_table[cur_user]); + + out[j] = handle_police_func(task, mbufs[j], cur_tsc, task->user_table[user[j]]); + } + + for (; j + PHASE3_DELAY < n_pkts; ++j) { + cur_user = get_user(task, mbufs[j + PHASE3_DELAY]); + user[j + PHASE3_DELAY] = cur_user; + PREFETCH0(&task->user_table[cur_user]); + + out[j] = handle_police_func(task, mbufs[j], cur_tsc, task->user_table[user[j]]); + } + + for (; j < n_pkts; ++j) { + out[j] = handle_police_func(task, mbufs[j], cur_tsc, task->user_table[user[j]]); + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int handle_police_bulk(struct task_base *tbase, struct rte_mbuf **mbuf, uint16_t n_pkts) +{ + return handle_pb(tbase, mbuf, n_pkts, handle_police); +} + +static int handle_police_tr_bulk(struct task_base *tbase, struct rte_mbuf **mbuf, uint16_t n_pkts) +{ + return handle_pb(tbase, mbuf, n_pkts, handle_police_tr); +} + +static void init_task_police(struct task_base *tbase, struct task_args *targ) +{ + struct task_police *task = (struct task_police *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->overhead = targ->overhead; + task->runtime_flags = targ->runtime_flags; + + task->user_table = prox_sh_find_socket(socket_id, "user_table"); + if (!task->user_table) { + PROX_PANIC(!strcmp(targ->user_table, ""), "No user table defined\n"); + int ret = lua_to_user_table(prox_lua(), GLOBAL, targ->user_table, socket_id, &task->user_table); + PROX_PANIC(ret, "Failed to create user table from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, "user_table", task->user_table); + } + + if (strcmp(targ->task_init->sub_mode_str, "trtcm")) { + task->sr_flows = prox_zmalloc(targ->n_flows * sizeof(*task->sr_flows), socket_id); + PROX_PANIC(task->sr_flows == NULL, "Failed to allocate flow contexts\n"); + PROX_PANIC(!targ->cir, "Commited information rate is set to 0\n"); + PROX_PANIC(!targ->cbs, "Commited information bucket size is set to 0\n"); + PROX_PANIC(!targ->ebs, "Execess information bucket size is set to 0\n"); + + struct rte_meter_srtcm_params params = { + .cir = targ->cir, + .cbs = targ->cbs, + .ebs = targ->ebs, + }; + + for (uint32_t i = 0; i < targ->n_flows; ++i) { + rte_meter_srtcm_config(&task->sr_flows[i], ¶ms); + } + } + else { + task->tr_flows = prox_zmalloc(targ->n_flows * sizeof(*task->tr_flows), socket_id); + PROX_PANIC(task->tr_flows == NULL, "Failed to allocate flow contexts\n"); + PROX_PANIC(!targ->pir, "Peak information rate is set to 0\n"); + PROX_PANIC(!targ->cir, "Commited information rate is set to 0\n"); + PROX_PANIC(!targ->pbs, "Peak information bucket size is set to 0\n"); + PROX_PANIC(!targ->cbs, "Commited information bucket size is set to 0\n"); + + struct rte_meter_trtcm_params params = { + .pir = targ->pir, + .pbs = targ->pbs, + .cir = targ->cir, + .cbs = targ->cbs, + }; + + for (uint32_t i = 0; i < targ->n_flows; ++i) { + rte_meter_trtcm_config(&task->tr_flows[i], ¶ms); + } + } + + for (uint32_t i = 0; i < 3; ++i) { + for (uint32_t j = 0; j < 3; ++j) { + task->police_act[i][j] = targ->police_act[i][j]; + } + } +} + +static struct task_init task_init_police = { + .mode_str = "police", + .init = init_task_police, + .handle = handle_police_bulk, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_police) +}; + +static struct task_init task_init_police2 = { + .mode_str = "police", + .sub_mode_str = "trtcm", + .init = init_task_police, + .handle = handle_police_tr_bulk, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_police) +}; + +__attribute__((constructor)) static void reg_task_police(void) +{ + reg_task(&task_init_police); + reg_task(&task_init_police2); +} diff --git a/VNFs/DPPD-PROX/handle_qinq_decap4.c b/VNFs/DPPD-PROX/handle_qinq_decap4.c new file mode 100644 index 00000000..f5c80227 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_decap4.c @@ -0,0 +1,659 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_byteorder.h> +#include <rte_cycles.h> +#include <rte_table_hash.h> +#include <rte_lpm.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" +#include "handle_qinq_decap4.h" +#include "handle_qinq_encap4.h" +#include "stats.h" +#include "tx_pkt.h" +#include "defines.h" +#include "handle_routing.h" +#include "prox_assert.h" +#include "task_init.h" +#include "quit.h" +#include "pkt_prototypes.h" +#include "task_base.h" +#include "task_init.h" +#include "bng_pkts.h" +#include "prox_cksum.h" +#include "expire_cpe.h" +#include "prox_port_cfg.h" +#include "prefetch.h" +#include "prox_cfg.h" +#include "lconf.h" +#include "prox_cfg.h" +#include "prox_shared.h" + +struct task_qinq_decap4 { + struct task_base base; + struct rte_table_hash *cpe_table; + struct rte_table_hash *qinq_gre_table; + struct qinq_gre_data *qinq_gre_data; + struct next_hop *next_hops; + struct rte_lpm *ipv4_lpm; + uint32_t local_ipv4; + uint16_t qinq_tag; + uint8_t runtime_flags; + int offload_crc; + uint64_t keys[64]; + uint64_t src_mac[PROX_MAX_PORTS]; + struct rte_mbuf* fake_packets[64]; + struct expire_cpe expire_cpe; + uint64_t cpe_timeout; + uint8_t mapping[PROX_MAX_PORTS]; +}; + +static uint8_t handle_qinq_decap4(struct task_qinq_decap4 *task, struct rte_mbuf *mbuf, struct qinq_gre_data* entry); +/* Convert IPv4 packets to GRE and optionally store QinQ Tags */ +static void arp_update(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +static void arp_msg(struct task_base *tbase, void **data, uint16_t n_msgs); + +static void init_task_qinq_decap4(struct task_base *tbase, struct task_args *targ) +{ + struct task_qinq_decap4 *task = (struct task_qinq_decap4 *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + struct lpm4 *lpm; + + task->cpe_table = targ->cpe_table; + task->cpe_timeout = msec_to_tsc(targ->cpe_table_timeout_ms); + + PROX_PANIC(!strcmp(targ->route_table, ""), "route table not specified\n"); + lpm = prox_sh_find_socket(socket_id, targ->route_table); + if (!lpm) { + int ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm); + PROX_PANIC(ret, "Failed to load IPv4 LPM:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->route_table, lpm); + } + task->ipv4_lpm = lpm->rte_lpm; + task->next_hops = lpm->next_hops; + + task->qinq_tag = targ->qinq_tag; + task->local_ipv4 = targ->local_ipv4; + task->runtime_flags = targ->runtime_flags; + if (strcmp(targ->task_init->sub_mode_str, "pe")) + PROX_PANIC(targ->qinq_gre_table == NULL, "can't set up qinq gre\n"); + + task->qinq_gre_table = targ->qinq_gre_table; + + if (targ->cpe_table_timeout_ms) { + targ->lconf->period_func = check_expire_cpe; + task->expire_cpe.cpe_table = task->cpe_table; + targ->lconf->period_data = &task->expire_cpe; + targ->lconf->period_timeout = msec_to_tsc(500) / NUM_VCPES; + } + + for (uint32_t i = 0; i < 64; ++i) { + task->fake_packets[i] = (struct rte_mbuf*)((uint8_t*)&task->keys[i] - sizeof (struct rte_mbuf)); + } + if (task->runtime_flags & TASK_ROUTING) { + if (targ->nb_txrings) { + struct task_args *dtarg; + struct core_task ct; + + for (uint32_t i = 0; i < targ->nb_txrings; ++i) { + ct = targ->core_task_set[0].core_task[i]; + dtarg = core_targ_get(ct.core, ct.task); + dtarg = find_reachable_task_sending_to_port(dtarg); + + PROX_PANIC(dtarg == NULL, "Error finding destination port through other tasks for outgoing ring %u\n", i); + task->src_mac[i] = *(uint64_t*)&prox_port_cfg[dtarg->tx_port_queue[0].port].eth_addr; + } + } + else { + for (uint32_t i = 0; i < targ->nb_txports; ++i) { + task->src_mac[i] = *(uint64_t*)&prox_port_cfg[targ->tx_port_queue[i].port].eth_addr; + } + } + } + + if (targ->runtime_flags & TASK_CTRL_HANDLE_ARP) { + targ->lconf->ctrl_func_p[targ->task] = arp_update; + } + + /* Copy the mapping from a sibling task which is configured + with mode encap4. The mapping is constant, so it is faster + to apply it when entries are added (least common case) + instead of re-applying it for every packet (most common + case). */ + + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (QINQ_ENCAP4 == smode) { + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + task->mapping[i] = targ->lconf->targs[task_id].mapping[i]; + } + } + } + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } + + // By default, calling this function 1K times per second => 64K ARP per second max + // If 4 interfaces sending to here, = ~0.1% of workload. + // If receiving more ARP, they will be dropped, or will dramatically slow down LB if in "no drop" mode. + targ->lconf->ctrl_timeout = freq_to_tsc(targ->ctrl_freq); + targ->lconf->ctrl_func_m[targ->task] = arp_msg; +} + +static void early_init_table(struct task_args *targ) +{ + if (!targ->qinq_gre_table && !targ->cpe_table) { + init_qinq_gre_table(targ, get_qinq_gre_map(targ)); + init_cpe4_table(targ); + } +} + +static inline void extract_key_bulk(struct rte_mbuf **mbufs, uint16_t n_pkts, struct task_qinq_decap4 *task) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + extract_key_cpe(mbufs[j], &task->keys[j]); + } +} + +__attribute__((cold)) static void handle_error(struct rte_mbuf *mbuf) +{ + struct cpe_pkt *packet = rte_pktmbuf_mtod(mbuf, struct cpe_pkt *); +#ifdef USE_QINQ + uint64_t key = (*(uint64_t*)(((uint8_t *)packet) + 12)) & 0xFF0FFFFFFF0FFFFF; + uint32_t svlan = packet->qinq_hdr.svlan.vlan_tci; + uint32_t cvlan = packet->qinq_hdr.cvlan.vlan_tci; + + svlan = rte_be_to_cpu_16(svlan & 0xFF0F); + cvlan = rte_be_to_cpu_16(cvlan & 0xFF0F); +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + plogx_err("Can't convert key %016lx qinq %d|%d (%x|%x) to gre_id, rss=%x flags=%lx, status_err_len=%lx, L2Tag=%d type=%d\n", + key, svlan, cvlan, svlan, cvlan, mbuf->hash.rss, mbuf->ol_flags, mbuf->udata64, mbuf->vlan_tci_outer, mbuf->packet_type); +#else +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + plogx_err("Can't convert key %016lx qinq %d|%d (%x|%x) to gre_id, rss=%x flags=%lx, status_err_len=%lx, L2Tag=%d type=%d\n", + key, svlan, cvlan, svlan, cvlan, mbuf->hash.rss, mbuf->ol_flags, mbuf->udata64, mbuf->reserved, mbuf->packet_type); +#else + plogx_err("Can't convert key %016lx qinq %d|%d (%x|%x) to gre_id, flags=%x, L2Tag=%d\n", + key, svlan, cvlan, svlan, cvlan, mbuf->ol_flags, mbuf->reserved); +#endif +#endif +#else + plogx_err("Can't convert ip %x to gre_id\n", rte_bswap32(packet->ipv4_hdr.src_addr)); +#endif +} + +static int add_cpe_entry(struct rte_table_hash *hash, struct cpe_key *key, struct cpe_data *data) +{ + void* entry_in_hash; + int ret, key_found = 0; + + ret = rte_table_hash_key8_ext_dosig_ops. + f_add(hash, key, data, &key_found, &entry_in_hash); + if (unlikely(ret)) { + plogx_err("Failed to add key: ip %x, gre %x\n", key->ip, key->gre_id); + return 1; + } + return 0; +} + +static void extract_key_data_arp(struct rte_mbuf* mbuf, struct cpe_key* key, struct cpe_data* data, const struct qinq_gre_data* entry, uint64_t cpe_timeout, uint8_t* mapping) +{ + const struct cpe_packet_arp *packet = rte_pktmbuf_mtod(mbuf, const struct cpe_packet_arp *); + uint32_t svlan = packet->qinq_hdr.svlan.vlan_tci & 0xFF0F; + uint32_t cvlan = packet->qinq_hdr.cvlan.vlan_tci & 0xFF0F; + uint8_t port_id; + key->ip = packet->arp.data.spa; + key->gre_id = entry->gre_id; + + data->mac_port_8bytes = *((const uint64_t *)(&packet->qinq_hdr.s_addr)); + data->qinq_svlan = svlan; + data->qinq_cvlan = cvlan; +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + port_id = mbuf->port; + +#else + port_id = mbuf->pkt.in_port; +#endif + uint8_t mapped = mapping[port_id]; + data->mac_port.out_idx = mapping[port_id]; + + if (unlikely(mapped == 255)) { + /* This error only occurs if the system is configured incorrectly */ + plog_warn("Failed adding packet: unknown mapping for port %d", port_id); + data->mac_port.out_idx = 0; + } + + data->user = entry->user; + data->tsc = rte_rdtsc() + cpe_timeout; +} + +void arp_msg_to_str(char *str, struct arp_msg *msg) +{ + sprintf(str, "%u %u %u %u %u.%u.%u.%u %x:%x:%x:%x:%x:%x %u\n", + msg->data.mac_port.out_idx, msg->key.gre_id, msg->data.qinq_svlan, msg->data.qinq_cvlan, + msg->key.ip_bytes[0], msg->key.ip_bytes[1], msg->key.ip_bytes[2], msg->key.ip_bytes[3], + msg->data.mac_port_b[0], msg->data.mac_port_b[1], msg->data.mac_port_b[2], + msg->data.mac_port_b[3], msg->data.mac_port_b[4], msg->data.mac_port_b[5], msg->data.user); +} + +int str_to_arp_msg(struct arp_msg *msg, const char *str) +{ + uint32_t ip[4], interface, gre_id, svlan, cvlan, mac[6], user; + + int ret = sscanf(str, "%u %u %u %u %u.%u.%u.%u %x:%x:%x:%x:%x:%x %u", + &interface, &gre_id, &svlan, &cvlan, + ip, ip + 1, ip + 2, ip + 3, + mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, &user); + + for (uint8_t i = 0; i < 4; ++i) + msg->key.ip_bytes[i] = ip[i]; + msg->key.gre_id = gre_id; + + for (uint8_t i = 0; i < 4; ++i) + msg->data.mac_port_b[i] = mac[i]; + msg->data.qinq_svlan = svlan; + msg->data.qinq_cvlan = cvlan; + msg->data.user = user; + msg->data.mac_port.out_idx = interface; + + return ret != 15; +} + +void arp_update_from_msg(struct rte_table_hash * cpe_table, struct arp_msg **msgs, uint16_t n_msgs, uint64_t cpe_timeout) +{ + int ret, key_found = 0; + void* entry_in_hash; + + for (uint16_t i = 0; i < n_msgs; ++i) { + msgs[i]->data.tsc = rte_rdtsc() + cpe_timeout; + ret = rte_table_hash_key8_ext_dosig_ops. + f_add(cpe_table, &msgs[i]->key, &msgs[i]->data, &key_found, &entry_in_hash); + if (unlikely(ret)) { + plogx_err("Failed to add key %x, gre %x\n", msgs[i]->key.ip, msgs[i]->key.gre_id); + } + } +} + +static void arp_msg(struct task_base *tbase, void **data, uint16_t n_msgs) +{ + struct task_qinq_decap4 *task = (struct task_qinq_decap4 *)tbase; + struct arp_msg **msgs = (struct arp_msg **)data; + + arp_update_from_msg(task->cpe_table, msgs, n_msgs, task->cpe_timeout); +} + +static void arp_update(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_decap4 *task = (struct task_qinq_decap4 *)tbase; + + prefetch_pkts(mbufs, n_pkts); + extract_key_bulk(mbufs, n_pkts, task); + + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + uint64_t lookup_hit_mask = 0; + struct qinq_gre_data* entries[64]; + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->qinq_gre_table, task->fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + TASK_STATS_ADD_RX(&task->base.aux->stats, n_pkts); + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + handle_error(mbufs[j]); + rte_pktmbuf_free(mbufs[j]); + continue; + } + + struct cpe_key key; + struct cpe_data data; + + extract_key_data_arp(mbufs[j], &key, &data, entries[j], task->cpe_timeout, task->mapping); + + void* entry_in_hash; + int ret, key_found = 0; + + ret = rte_table_hash_key8_ext_dosig_ops. + f_add(task->cpe_table, &key, &data, &key_found, &entry_in_hash); + + if (unlikely(ret)) { + plogx_err("Failed to add key %x, gre %x\n", key.ip, key.gre_id); + TASK_STATS_ADD_DROP_DISCARD(&task->base.aux->stats, 1); + } + + /* should do ARP reply */ + TASK_STATS_ADD_DROP_HANDLED(&task->base.aux->stats, 1); + rte_pktmbuf_free(mbufs[j]); + } +} + +static int handle_qinq_decap4_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_decap4 *task = (struct task_qinq_decap4 *)tbase; + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + struct qinq_gre_data* entries[64]; + uint8_t out[MAX_PKT_BURST]; + uint64_t lookup_hit_mask; + prefetch_pkts(mbufs, n_pkts); + + // Prefetch headroom, as we will prepend mbuf and write to this cache line + for (uint16_t j = 0; j < n_pkts; ++j) { + PREFETCH0((rte_pktmbuf_mtod(mbufs[j], char*)-1)); + } + + extract_key_bulk(mbufs, n_pkts, task); + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->qinq_gre_table, task->fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (likely(lookup_hit_mask == pkts_mask)) { + for (uint16_t j = 0; j < n_pkts; ++j) { + out[j] = handle_qinq_decap4(task, mbufs[j], entries[j]); + } + } + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + // This might fail as the packet has not the expected QinQ or it's not an IPv4 packet + handle_error(mbufs[j]); + out[j] = OUT_DISCARD; + continue; + } + out[j] = handle_qinq_decap4(task, mbufs[j], entries[j]); + } + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +/* add gre header */ +static inline void gre_encap(struct task_qinq_decap4 *task, uint32_t src_ipv4, struct rte_mbuf *mbuf, uint32_t gre_id) +{ +#ifdef USE_QINQ + struct ipv4_hdr *pip = (struct ipv4_hdr *)(1 + rte_pktmbuf_mtod(mbuf, struct qinq_hdr *)); +#else + struct ipv4_hdr *pip = (struct ipv4_hdr *)(1 + rte_pktmbuf_mtod(mbuf, struct ether_hdr *)); +#endif + uint16_t ip_len = rte_be_to_cpu_16(pip->total_length); + uint16_t padlen = rte_pktmbuf_pkt_len(mbuf) - 20 - ip_len - sizeof(struct qinq_hdr); + + if (padlen) { + rte_pktmbuf_trim(mbuf, padlen); + } + + PROX_PANIC(rte_pktmbuf_data_len(mbuf) - padlen + 20 > ETHER_MAX_LEN, + "Would need to fragment packet new size = %u - not implemented\n", + rte_pktmbuf_data_len(mbuf) - padlen + 20); + +#ifdef USE_QINQ + /* prepend only 20 bytes instead of 28, 8 bytes are present from the QinQ */ + struct ether_hdr *peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, 20); +#else + struct ether_hdr *peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, 28); +#endif + + PROX_ASSERT(peth); + PREFETCH0(peth); + if (task->runtime_flags & TASK_TX_CRC) { + /* calculate IP CRC here to avoid problems with -O3 flag with gcc */ +#ifdef MPLS_ROUTING + prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr) + sizeof(struct mpls_hdr), sizeof(struct ipv4_hdr), task->offload_crc); +#else + prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); +#endif + } + + /* new IP header */ + struct ipv4_hdr *p_tunnel_ip = (struct ipv4_hdr *)(peth + 1); + rte_memcpy(p_tunnel_ip, &tunnel_ip_proto, sizeof(struct ipv4_hdr)); + ip_len += sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr); + p_tunnel_ip->total_length = rte_cpu_to_be_16(ip_len); + p_tunnel_ip->src_addr = src_ipv4; + + /* Add GRE Header values */ + struct gre_hdr *pgre = (struct gre_hdr *)(p_tunnel_ip + 1); + + rte_memcpy(pgre, &gre_hdr_proto, sizeof(struct gre_hdr)); + pgre->gre_id = gre_id; + peth->ether_type = ETYPE_IPv4; +} + +static inline uint16_t calc_padlen(const struct rte_mbuf *mbuf, const uint16_t ip_len) +{ + return rte_pktmbuf_pkt_len(mbuf) - DOWNSTREAM_DELTA - ip_len - offsetof(struct cpe_pkt, ipv4_hdr); +} + +static inline uint8_t gre_encap_route(uint32_t src_ipv4, struct rte_mbuf *mbuf, uint32_t gre_id, struct task_qinq_decap4 *task) +{ + PROX_PANIC(rte_pktmbuf_data_len(mbuf) + DOWNSTREAM_DELTA > ETHER_MAX_LEN, + "Would need to fragment packet new size = %u - not implemented\n", + rte_pktmbuf_data_len(mbuf) + DOWNSTREAM_DELTA); + + struct core_net_pkt_m *packet = (struct core_net_pkt_m *)rte_pktmbuf_prepend(mbuf, DOWNSTREAM_DELTA); + PROX_ASSERT(packet); + PREFETCH0(packet); + + struct ipv4_hdr *pip = &((struct cpe_pkt_delta *)packet)->pkt.ipv4_hdr; + uint16_t ip_len = rte_be_to_cpu_16(pip->total_length); + + /* returns 0 on success, returns -ENOENT of failure (or -EINVAL if first or last parameter is NULL) */ +#if RTE_VERSION >= RTE_VERSION_NUM(16,4,0,1) + uint32_t next_hop_index; +#else + uint8_t next_hop_index; +#endif + if (unlikely(rte_lpm_lookup(task->ipv4_lpm, rte_bswap32(pip->dst_addr), &next_hop_index) != 0)) { + plog_warn("lpm_lookup failed for ip %x: rc = %d\n", rte_bswap32(pip->dst_addr), -ENOENT); + return ROUTE_ERR; + } + PREFETCH0(&task->next_hops[next_hop_index]); + + /* calculate outer IP CRC here to avoid problems with -O3 flag with gcc */ + const uint16_t padlen = calc_padlen(mbuf, ip_len); + if (padlen) { + rte_pktmbuf_trim(mbuf, padlen); + } + const uint8_t port_id = task->next_hops[next_hop_index].mac_port.out_idx; + + *((uint64_t *)(&packet->ether_hdr.d_addr)) = task->next_hops[next_hop_index].mac_port_8bytes; + *((uint64_t *)(&packet->ether_hdr.s_addr)) = task->src_mac[task->next_hops[next_hop_index].mac_port.out_idx]; + +#ifdef MPLS_ROUTING + packet->mpls_bytes = task->next_hops[next_hop_index].mpls | 0x00010000; // Set BoS to 1 + packet->ether_hdr.ether_type = ETYPE_MPLSU; +#else + packet->ether_hdr.ether_type = ETYPE_IPv4; +#endif + + /* New IP header */ + rte_memcpy(&packet->tunnel_ip_hdr, &tunnel_ip_proto, sizeof(struct ipv4_hdr)); + ip_len += sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr); + packet->tunnel_ip_hdr.total_length = rte_cpu_to_be_16(ip_len); + packet->tunnel_ip_hdr.src_addr = src_ipv4; + packet->tunnel_ip_hdr.dst_addr = task->next_hops[next_hop_index].ip_dst; + if (task->runtime_flags & TASK_TX_CRC) { +#ifdef MPLS_ROUTING + prox_ip_cksum(mbuf, (void *)&(packet->tunnel_ip_hdr), sizeof(struct ether_hdr) + sizeof(struct mpls_hdr), sizeof(struct ipv4_hdr), task->offload_crc); +#else + prox_ip_cksum(mbuf, (void *)&(packet->tunnel_ip_hdr), sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); +#endif + } + + /* Add GRE Header values */ + rte_memcpy(&packet->gre_hdr, &gre_hdr_proto, sizeof(struct gre_hdr)); + packet->gre_hdr.gre_id = rte_be_to_cpu_32(gre_id); + + return port_id; +} + +static void extract_key_data(struct rte_mbuf* mbuf, struct cpe_key* key, struct cpe_data* data, const struct qinq_gre_data* entry, uint64_t cpe_timeout, uint8_t *mapping) +{ + struct cpe_pkt *packet = rte_pktmbuf_mtod(mbuf, struct cpe_pkt *); + uint8_t port_id; + +#ifndef USE_QINQ + const uint32_t tmp = rte_bswap32(packet->ipv4_hdr.src_addr) & 0x00FFFFFF; + const uint32_t svlan = rte_bswap16(tmp >> 12); + const uint32_t cvlan = rte_bswap16(tmp & 0x0FFF); +#endif + +#ifdef USE_QINQ + key->ip = packet->ipv4_hdr.src_addr; +#else + key->ip = 0; +#endif + key->gre_id = entry->gre_id; + +#ifdef USE_QINQ + data->mac_port_8bytes = *((const uint64_t *)(&packet->qinq_hdr.s_addr)); + data->qinq_svlan = packet->qinq_hdr.svlan.vlan_tci & 0xFF0F; + data->qinq_cvlan = packet->qinq_hdr.cvlan.vlan_tci & 0xFF0F; +#else + data->mac_port_8bytes = *((const uint64_t *)(&packet->ether_hdr.s_addr)); + data->qinq_svlan = svlan; + data->qinq_cvlan = cvlan; +#endif + +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + port_id = mbuf->port; + +#else + port_id = mbuf->pkt.in_port; +#endif + uint8_t mapped = mapping[port_id]; + data->mac_port.out_idx = mapped; + + if (unlikely(mapped == 255)) { + /* This error only occurs if the system is configured incorrectly */ + plog_warn("Failed adding packet: unknown mapping for port %d", port_id); + data->mac_port.out_idx = 0; + } + else { + data->mac_port.out_idx = mapped; + } + + data->user = entry->user; + data->tsc = rte_rdtsc() + cpe_timeout; +} + +static uint8_t handle_qinq_decap4(struct task_qinq_decap4 *task, struct rte_mbuf *mbuf, struct qinq_gre_data* entry) +{ + if (!(task->runtime_flags & (TASK_CTRL_HANDLE_ARP|TASK_FP_HANDLE_ARP))) { + // We learn CPE MAC addresses on every packets + struct cpe_key key; + struct cpe_data data; + extract_key_data(mbuf, &key, &data, entry, task->cpe_timeout, task->mapping); + //plogx_err("Adding key ip=%x/gre_id=%x data (svlan|cvlan)=%x|%x, rss=%x, gre_id=%x\n", key.ip, key.gre_id, data.qinq_svlan,data.qinq_cvlan, mbuf->hash.rss, entry->gre_id); + + if (add_cpe_entry(task->cpe_table, &key, &data)) { + plog_warn("Failed to add ARP entry\n"); + return OUT_DISCARD; + } + } + if (task->runtime_flags & TASK_FP_HANDLE_ARP) { + // We learn CPE MAC addresses on ARP packets in Fast Path +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + if (mbuf->packet_type == 0xB) { + struct cpe_key key; + struct cpe_data data; + extract_key_data_arp(mbuf, &key, &data, entry, task->cpe_timeout, task->mapping); + + if (add_cpe_entry(task->cpe_table, &key, &data)) { + plog_warn("Failed to add ARP entry\n"); + return OUT_DISCARD; + } + return OUT_HANDLED; + } else +#endif + { +#ifdef USE_QINQ + struct cpe_pkt *packet = rte_pktmbuf_mtod(mbuf, struct cpe_pkt*); + if (packet->qinq_hdr.svlan.eth_proto == task->qinq_tag && + packet->qinq_hdr.ether_type == ETYPE_ARP) { + struct cpe_key key; + struct cpe_data data; + extract_key_data_arp(mbuf, &key, &data, entry, task->cpe_timeout, task->mapping); + + if (add_cpe_entry(task->cpe_table, &key, &data)) { + plog_warn("Failed to add ARP entry\n"); + return OUT_DISCARD; + } + return OUT_HANDLED; + } +#endif + } + } + if (task->runtime_flags & TASK_ROUTING) { + uint8_t tx_portid; + tx_portid = gre_encap_route(task->local_ipv4, mbuf, entry->gre_id, task); + + return tx_portid == ROUTE_ERR? OUT_DISCARD : tx_portid; + } + else { + gre_encap(task, task->local_ipv4, mbuf, entry->gre_id); + return 0; + } +} + +static void flow_iter_next(struct flow_iter *iter, struct task_args *targ) +{ + do { + iter->idx++; + } while (iter->idx < (int)get_qinq_gre_map(targ)->count && + get_qinq_gre_map(targ)->entries[iter->idx].gre_id % targ->nb_slave_threads != targ->worker_thread_id); +} + +static void flow_iter_beg(struct flow_iter *iter, struct task_args *targ) +{ + iter->idx = -1; + flow_iter_next(iter, targ); +} + +static int flow_iter_is_end(struct flow_iter *iter, struct task_args *targ) +{ + return iter->idx == (int)get_qinq_gre_map(targ)->count; +} + +static uint16_t flow_iter_get_svlan(struct flow_iter *iter, struct task_args *targ) +{ + return get_qinq_gre_map(targ)->entries[iter->idx].svlan; +} + +static uint16_t flow_iter_get_cvlan(struct flow_iter *iter, struct task_args *targ) +{ + return get_qinq_gre_map(targ)->entries[iter->idx].cvlan; +} + +static struct task_init task_init_qinq_decapv4_table = { + .mode = QINQ_DECAP4, + .mode_str = "qinqdecapv4", + .early_init = early_init_table, + .init = init_task_qinq_decap4, + .handle = handle_qinq_decap4_bulk, + .flag_features = TASK_FEATURE_ROUTING, + .flow_iter = { + .beg = flow_iter_beg, + .is_end = flow_iter_is_end, + .next = flow_iter_next, + .get_svlan = flow_iter_get_svlan, + .get_cvlan = flow_iter_get_cvlan, + }, + .size = sizeof(struct task_qinq_decap4) +}; + +__attribute__((constructor)) static void reg_task_qinq_decap4(void) +{ + reg_task(&task_init_qinq_decapv4_table); +} diff --git a/VNFs/DPPD-PROX/handle_qinq_decap4.h b/VNFs/DPPD-PROX/handle_qinq_decap4.h new file mode 100644 index 00000000..ae2475d2 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_decap4.h @@ -0,0 +1,34 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_QINQ_DECAP4_H_ +#define _HANDLE_QINQ_DECAP4_H_ + +#include "hash_entry_types.h" + +struct rte_table_hash; + +struct arp_msg { + struct cpe_key key; + struct cpe_data data; +}; + +void arp_msg_to_str(char *str, struct arp_msg *msg); +int str_to_arp_msg(struct arp_msg *msg, const char *str); + +void arp_update_from_msg(struct rte_table_hash * cpe_table, struct arp_msg **msgs, uint16_t n_msgs, uint64_t cpe_timeout); + +#endif /* _HANDLE_QINQ_DECAP4_H_ */ diff --git a/VNFs/DPPD-PROX/handle_qinq_decap6.c b/VNFs/DPPD-PROX/handle_qinq_decap6.c new file mode 100644 index 00000000..d876c733 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_decap6.c @@ -0,0 +1,197 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_table_hash.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "handle_qinq_encap6.h" +#include "log.h" +#include "lconf.h" +#include "task_init.h" +#include "task_base.h" +#include "tx_pkt.h" +#include "defines.h" +#include "pkt_prototypes.h" +#include "prox_assert.h" +#include "hash_utils.h" +#include "task_base.h" +#include "prefetch.h" +#include "hash_entry_types.h" +#include "prox_cfg.h" +#include "log.h" +#include "quit.h" +#include "prox_shared.h" + +/* Packets must all be IPv6, always store QinQ tags for lookup (not configurable) */ +struct task_qinq_decap6 { + struct task_base base; + struct rte_table_hash *cpe_table; + uint16_t *user_table; + uint32_t bucket_index; + struct ether_addr edaddr; + struct rte_lpm6 *rte_lpm6; + void* period_data; /* used if using dual stack*/ + void (*period_func)(void* data); + uint64_t cpe_timeout; +}; + +void update_arp_entries6(void* data); + +static void init_task_qinq_decap6(struct task_base *tbase, struct task_args *targ) +{ + struct task_qinq_decap6 *task = (struct task_qinq_decap6 *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->edaddr = targ->edaddr; + task->cpe_table = targ->cpe_table; + task->cpe_timeout = msec_to_tsc(targ->cpe_table_timeout_ms); + + if (targ->cpe_table_timeout_ms) { + if (targ->lconf->period_func) { + task->period_func = targ->lconf->period_func; + task->period_data = targ->lconf->period_data; + } + targ->lconf->period_func = update_arp_entries6; + targ->lconf->period_data = tbase; + targ->lconf->period_timeout = msec_to_tsc(500) / NUM_VCPES; + } + + task->user_table = prox_sh_find_socket(socket_id, "user_table"); + if (!task->user_table) { + PROX_PANIC(!strcmp(targ->user_table, ""), "No user table defined\n"); + int ret = lua_to_user_table(prox_lua(), GLOBAL, targ->user_table, socket_id, &task->user_table); + PROX_PANIC(ret, "Failed to create user table from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, "user_table", task->user_table); + } + + struct lpm6 *lpm = prox_sh_find_socket(socket_id, "lpm6"); + if (!lpm) { + struct lpm6 *lpm6; + int ret; + + ret = lua_to_lpm6(prox_lua(), GLOBAL, "lpm6", socket_id, &lpm6); + PROX_PANIC(ret, "Failed to read lpm6 from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, "lpm6", lpm6); + } + task->rte_lpm6 = lpm->rte_lpm6; +} + +static void early_init(struct task_args *targ) +{ + if (!targ->cpe_table) { + init_cpe6_table(targ); + } +} + +static inline uint8_t handle_qinq_decap6(struct task_qinq_decap6 *task, struct rte_mbuf *mbuf) +{ + struct qinq_hdr *pqinq = rte_pktmbuf_mtod(mbuf, struct qinq_hdr *); + struct ipv6_hdr *pip6 = (struct ipv6_hdr *)(pqinq + 1); + + uint16_t svlan = pqinq->svlan.vlan_tci & 0xFF0F; + uint16_t cvlan = pqinq->cvlan.vlan_tci & 0xFF0F; + + struct cpe_data entry; + entry.mac_port_8bytes = *((uint64_t *)(((uint8_t *)pqinq) + 5)) << 16; + entry.qinq_svlan = svlan; + entry.qinq_cvlan = cvlan; + entry.user = task->user_table[PKT_TO_LUTQINQ(svlan, cvlan)]; + entry.tsc = rte_rdtsc() + task->cpe_timeout; + + int key_found = 0; + void* entry_in_hash = NULL; + int ret = rte_table_hash_ext_dosig_ops. + f_add(task->cpe_table, pip6->src_addr, &entry, &key_found, &entry_in_hash); + + if (unlikely(ret)) { + plogx_err("Failed to add key " IPv6_BYTES_FMT "\n", IPv6_BYTES(pip6->src_addr)); + return OUT_DISCARD; + } + + pqinq = (struct qinq_hdr *)rte_pktmbuf_adj(mbuf, 2 * sizeof(struct vlan_hdr)); + PROX_ASSERT(pqinq); + pqinq->ether_type = ETYPE_IPv6; + // Dest MAC addresses + ether_addr_copy(&task->edaddr, &pqinq->d_addr); + return 0; +} + +static int handle_qinq_decap6_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_decap6 *task = (struct task_qinq_decap6 *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_qinq_decap6(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_qinq_decap6(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +void update_arp_entries6(void* data) +{ + uint64_t cur_tsc = rte_rdtsc(); + struct task_qinq_decap6 *task = (struct task_qinq_decap6 *)data; + + struct cpe_data *entries[4] = {0}; + void *key[4] = {0}; + uint64_t n_buckets = get_bucket(task->cpe_table, task->bucket_index, key, (void**)entries); + + for (uint8_t i = 0; i < 4 && entries[i]; ++i) { + if (entries[i]->tsc < cur_tsc) { + int key_found = 0; + void* entry = 0; + rte_table_hash_ext_dosig_ops.f_delete(task->cpe_table, key[i], &key_found, entry); + } + } + + task->bucket_index++; + task->bucket_index &= (n_buckets - 1); + + if (task->period_func) { + task->period_func(task->period_data); + } +} + +static struct task_init task_init_qinq_decap6 = { + .mode = QINQ_DECAP6, + .mode_str = "qinqdecapv6", + .early_init = early_init, + .init = init_task_qinq_decap6, + .handle = handle_qinq_decap6_bulk, + .size = sizeof(struct task_qinq_decap6) +}; + +__attribute__((constructor)) static void reg_task_qinq_decap6(void) +{ + reg_task(&task_init_qinq_decap6); +} diff --git a/VNFs/DPPD-PROX/handle_qinq_encap4.c b/VNFs/DPPD-PROX/handle_qinq_encap4.c new file mode 100644 index 00000000..24181959 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_encap4.c @@ -0,0 +1,662 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_table_hash.h> +#include <rte_cycles.h> + +#include "mbuf_utils.h" +#include "prox_malloc.h" +#include "prox_lua.h" +#include "prox_lua_types.h" +#include "handle_qinq_encap4.h" +#include "handle_qinq_decap4.h" +#include "prox_args.h" +#include "defines.h" +#include "tx_pkt.h" +#include "prefetch.h" +#include "pkt_prototypes.h" +#include "hash_entry_types.h" +#include "task_init.h" +#include "bng_pkts.h" +#include "prox_cksum.h" +#include "hash_utils.h" +#include "quit.h" +#include "prox_port_cfg.h" +#include "handle_lb_net.h" +#include "prox_cfg.h" +#include "cfgfile.h" +#include "toeplitz.h" +#include "prox_shared.h" + +static struct cpe_table_data *read_cpe_table_config(const char *name, uint8_t socket) +{ + struct lua_State *L = prox_lua(); + struct cpe_table_data *ret = NULL; + + lua_getglobal(L, name); + PROX_PANIC(lua_isnil(L, -1), "Coudn't find cpe_table data\n"); + + return ret; +} + +struct qinq_gre_map *get_qinq_gre_map(struct task_args *targ) +{ + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + struct qinq_gre_map *ret = prox_sh_find_socket(socket_id, "qinq_gre_map"); + + if (!ret) { + PROX_PANIC(!strcmp(targ->user_table, ""), "No user table defined\n"); + int rv = lua_to_qinq_gre_map(prox_lua(), GLOBAL, targ->user_table, socket_id, &ret); + PROX_PANIC(rv, "Error reading mapping between qinq and gre from qinq_gre_map: \n%s\n", + get_lua_to_errors()); + prox_sh_add_socket(socket_id, "qinq_gre_map", ret); + } + return ret; +} + +/* Encapsulate IPv4 packets in QinQ. QinQ tags are derived from gre_id. */ +int handle_qinq_encap4_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +static void arp_msg(struct task_base *tbase, void **data, uint16_t n_msgs); + +static void fill_table(struct task_args *targ, struct rte_table_hash *table) +{ + struct cpe_table_data *cpe_table_data; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + int ret = lua_to_cpe_table_data(prox_lua(), GLOBAL, targ->cpe_table_name, socket_id, &cpe_table_data); + const uint8_t n_slaves = targ->nb_slave_threads; + const uint8_t worker_id = targ->worker_thread_id; + + for (uint32_t i = 0; i < cpe_table_data->n_entries; ++i) { + if (rte_bswap32(cpe_table_data->entries[i].ip) % n_slaves != worker_id) { + continue; + } + struct cpe_table_entry *entry = &cpe_table_data->entries[i]; + + uint32_t port_idx = prox_cfg.cpe_table_ports[entry->port_idx]; + PROX_PANIC(targ->mapping[port_idx] == 255, "Error reading cpe table: Mapping for port %d is missing", port_idx); + + struct cpe_key key = { + .ip = entry->ip, + .gre_id = entry->gre_id, + }; + + struct cpe_data data = { + .qinq_svlan = entry->svlan, + .qinq_cvlan = entry->cvlan, + .user = entry->user, + .mac_port = { + .mac = entry->eth_addr, + .out_idx = targ->mapping[port_idx], + }, + .tsc = UINT64_MAX, + }; + + int key_found; + void* entry_in_hash; + rte_table_hash_key8_ext_dosig_ops.f_add(table, &key, &data, &key_found, &entry_in_hash); + } +} + +static void init_task_qinq_encap4(struct task_base *tbase, struct task_args *targ) +{ + struct task_qinq_encap4 *task = (struct task_qinq_encap4 *)(tbase); + int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + + task->qinq_tag = targ->qinq_tag; + task->cpe_table = targ->cpe_table; + task->cpe_timeout = msec_to_tsc(targ->cpe_table_timeout_ms); + + if (!strcmp(targ->task_init->sub_mode_str, "pe")) { + PROX_PANIC(!strcmp(targ->cpe_table_name, ""), "CPE table not configured\n"); + fill_table(targ, task->cpe_table); + } + +#ifdef ENABLE_EXTRA_USER_STATISTICS + task->n_users = targ->n_users; + task->stats_per_user = prox_zmalloc(targ->n_users * sizeof(uint32_t), socket_id); +#endif + if (targ->runtime_flags & TASK_CLASSIFY) { + PROX_PANIC(!strcmp(targ->dscp, ""), "DSCP table not specified\n"); + task->dscp = prox_sh_find_socket(socket_id, targ->dscp); + if (!task->dscp) { + int ret = lua_to_dscp(prox_lua(), GLOBAL, targ->dscp, socket_id, &task->dscp); + PROX_PANIC(ret, "Failed to create dscp table from config:\n%s\n", + get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->dscp, task->dscp); + } + } + + task->runtime_flags = targ->runtime_flags; + + for (uint32_t i = 0; i < 64; ++i) { + task->fake_packets[i] = (struct rte_mbuf*)((uint8_t*)&task->keys[i] - sizeof (struct rte_mbuf)); + } + + targ->lconf->ctrl_timeout = freq_to_tsc(targ->ctrl_freq); + targ->lconf->ctrl_func_m[targ->task] = arp_msg; + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } + + /* TODO: check if it is not necessary to limit reverse mapping + for the elements that have been changing in mapping? */ + + for (uint32_t i =0 ; i < sizeof(targ->mapping)/sizeof(targ->mapping[0]); ++i) { + task->src_mac[targ->mapping[i]] = *(uint64_t*)&prox_port_cfg[i].eth_addr; + } + + /* task->src_mac[entry->port_idx] = *(uint64_t*)&prox_port_cfg[entry->port_idx].eth_addr; */ +} + +static void arp_msg(struct task_base *tbase, void **data, uint16_t n_msgs) +{ + struct task_qinq_encap4 *task = (struct task_qinq_encap4 *)tbase; + struct arp_msg **msgs = (struct arp_msg **)data; + + arp_update_from_msg(task->cpe_table, msgs, n_msgs, task->cpe_timeout); +} + +static inline void add_key(struct task_args *targ, struct qinq_gre_map *qinq_gre_map, struct rte_table_hash* qinq_gre_table, uint32_t i, uint32_t *count) +{ + struct qinq_gre_data entry = { + .gre_id = qinq_gre_map->entries[i].gre_id, + .user = qinq_gre_map->entries[i].user, + }; + +#ifdef USE_QINQ + struct vlans qinq2 = { + .svlan = {.eth_proto = targ->qinq_tag, .vlan_tci = qinq_gre_map->entries[i].svlan}, + .cvlan = {.eth_proto = ETYPE_VLAN, .vlan_tci = qinq_gre_map->entries[i].cvlan} + }; + + int key_found = 0; + void* entry_in_hash = NULL; + rte_table_hash_key8_ext_dosig_ops.f_add(qinq_gre_table, &qinq2, &entry, &key_found, &entry_in_hash); + + plog_dbg("Core %u adding user %u (tag %x svlan %x cvlan %x), rss=%x\n", + targ->lconf->id, qinq_gre_map->entries[i].user, qinq2.svlan.eth_proto, + rte_bswap16(qinq_gre_map->entries[i].svlan), + rte_bswap16(qinq_gre_map->entries[i].cvlan), + qinq_gre_map->entries[i].rss); +#else + /* lower 3 bytes of IPv4 address contain svlan/cvlan. */ + uint64_t ip = ((uint32_t)rte_bswap16(qinq_gre_map->entries[i].svlan) << 12) | + rte_bswap16(qinq_gre_map->entries[i].cvlan); + int key_found = 0; + void* entry_in_hash = NULL; + rte_table_hash_key8_ext_dosig_ops.f_add(qinq_gre_table, &ip, &entry, &key_found, &entry_in_hash); + + plog_dbg("Core %u hash table add: key = %016"PRIx64"\n", + targ->lconf->id, ip); +#endif + (*count)++; +} + +void init_qinq_gre_table(struct task_args *targ, struct qinq_gre_map *qinq_gre_map) +{ + struct rte_table_hash* qinq_gre_table; + uint8_t table_part = targ->nb_slave_threads; + if (!rte_is_power_of_2(table_part)) { + table_part = rte_align32pow2(table_part) >> 1; + } + + if (table_part == 0) + table_part = 1; + + uint32_t n_entries = MAX_GRE / table_part; + + struct rte_table_hash_key8_ext_params table_hash_params = { + .n_entries = n_entries, + .n_entries_ext = n_entries >> 1, + .f_hash = hash_crc32, + .seed = 0, + .signature_offset = HASH_METADATA_OFFSET(8), + .key_offset = HASH_METADATA_OFFSET(0), + }; + + qinq_gre_table = rte_table_hash_key8_ext_dosig_ops. + f_create(&table_hash_params, rte_lcore_to_socket_id(targ->lconf->id), sizeof(struct qinq_gre_data)); + + // LB configuration known from Network Load Balancer + // Find LB network Load balancer, i.e. ENCAP friend. + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (QINQ_ENCAP4 == smode) { + targ->lb_friend_core = targ->lconf->targs[task_id].lb_friend_core; + targ->lb_friend_task = targ->lconf->targs[task_id].lb_friend_task; + } + } + // Packet coming from Load balancer. LB could balance on gre_id LSB, qinq hash or qinq RSS + uint32_t flag_features = 0; + if (targ->lb_friend_core != 0xFF) { + struct task_args *lb_targ = &lcore_cfg[targ->lb_friend_core].targs[targ->lb_friend_task]; + flag_features = lb_targ->task_init->flag_features; + plog_info("\t\tWT %d Updated features to %x from friend %d\n", targ->lconf->id, flag_features, targ->lb_friend_core); + } else { + plog_info("\t\tWT %d has no friend\n", targ->lconf->id); + } + if (targ->nb_slave_threads == 0) { + // No slave threads, i.e. using RSS + plog_info("feature was %x is now %x\n", flag_features, TASK_FEATURE_LUT_QINQ_RSS); + flag_features = TASK_FEATURE_LUT_QINQ_RSS; + } + if ((flag_features & (TASK_FEATURE_GRE_ID|TASK_FEATURE_LUT_QINQ_RSS|TASK_FEATURE_LUT_QINQ_HASH)) == 0) { + plog_info("\t\tCould not find flag feature from Load balancer => supposing TASK_FEATURE_GRE_ID\n"); + flag_features = TASK_FEATURE_GRE_ID; + } + + /* Only store QinQ <-> GRE mapping for packets that are handled by this worker thread */ + uint32_t count = 0; + if (flag_features & TASK_FEATURE_LUT_QINQ_RSS) { + // If there is a load balancer, number of worker thread is indicated by targ->nb_slave_threads and n_rxq = 0 + // If there is no load balancers, number of worker thread is indicated by n_rxq and nb_slave_threads = 0 + uint8_t nb_worker_threads, worker_thread_id; + if (targ->nb_slave_threads) { + nb_worker_threads = targ->nb_slave_threads; + worker_thread_id = targ->worker_thread_id; + } else if (prox_port_cfg[targ->rx_port_queue[0].port].n_rxq) { + nb_worker_threads = prox_port_cfg[targ->rx_port_queue[0].port].n_rxq; + worker_thread_id = targ->rx_port_queue[0].queue; + } else { + PROX_PANIC(1, "Unexpected: unknown number of worker thread\n"); + } + plog_info("\t\tUsing %d worker_threads id %d\n", nb_worker_threads, worker_thread_id); + for (uint32_t i = 0; i < qinq_gre_map->count; ++i) { + if (targ->nb_slave_threads == 0 || rss_to_queue(qinq_gre_map->entries[i].rss, nb_worker_threads) == worker_thread_id) { + add_key(targ, qinq_gre_map, qinq_gre_table, i, &count); + //plog_info("Queue %d adding key %16lx, svlan %x cvlan %x, rss=%x\n", targ->rx_queue, *(uint64_t *)q, qinq_to_gre_lookup[i].svlan, qinq_to_gre_lookup[i].cvlan, qinq_to_gre_lookup[i].rss); + } + } + plog_info("\t\tAdded %d entries to worker thread %d\n", count, worker_thread_id); + } else if (flag_features & TASK_FEATURE_LUT_QINQ_HASH) { + for (uint32_t i = 0; i < qinq_gre_map->count; ++i) { + uint64_t cvlan = rte_bswap16(qinq_gre_map->entries[i].cvlan & 0xFF0F); + uint64_t svlan = rte_bswap16((qinq_gre_map->entries[i].svlan & 0xFF0F)); + uint64_t qinq = rte_bswap64((svlan << 32) | cvlan); + uint8_t queue = hash_crc32(&qinq, 8, 0) % targ->nb_slave_threads; + if (queue == targ->worker_thread_id) { + add_key(targ, qinq_gre_map, qinq_gre_table, i, &count); + } + } + plog_info("\t\tAdded %d entries to WT %d\n", count, targ->worker_thread_id); + } else if (flag_features & TASK_FEATURE_GRE_ID) { + for (uint32_t i = 0; i < qinq_gre_map->count; ++i) { + if (qinq_gre_map->entries[i].gre_id % targ->nb_slave_threads == targ->worker_thread_id) { + add_key(targ, qinq_gre_map, qinq_gre_table, i, &count); + } + } + } + + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (QINQ_DECAP4 == smode) { + targ->lconf->targs[task_id].qinq_gre_table = qinq_gre_table; + } + + } +} + +void init_cpe4_table(struct task_args *targ) +{ + char name[64]; + sprintf(name, "core_%u_CPEv4Table", targ->lconf->id); + + uint8_t table_part = targ->nb_slave_threads; + if (!rte_is_power_of_2(table_part)) { + table_part = rte_align32pow2(table_part) >> 1; + } + + if (table_part == 0) + table_part = 1; + + uint32_t n_entries = MAX_GRE / table_part; + struct rte_table_hash_key8_ext_params table_hash_params = { + .n_entries = n_entries, + .n_entries_ext = n_entries >> 1, + .f_hash = hash_crc32, + .seed = 0, + .signature_offset = HASH_METADATA_OFFSET(8), + .key_offset = HASH_METADATA_OFFSET(0), + }; + size_t entry_size = sizeof(struct cpe_data); + if (!rte_is_power_of_2(entry_size)) { + entry_size = rte_align32pow2(entry_size); + } + + struct rte_table_hash* phash = rte_table_hash_key8_ext_dosig_ops. + f_create(&table_hash_params, rte_lcore_to_socket_id(targ->lconf->id), entry_size); + PROX_PANIC(NULL == phash, "Unable to allocate memory for IPv4 hash table on core %u\n", targ->lconf->id); + + /* for locality, copy the pointer to the port structure where it is needed at packet handling time */ + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (QINQ_ENCAP4 == smode || QINQ_DECAP4 == smode) { + targ->lconf->targs[task_id].cpe_table = phash; + } + } +} + +static void early_init_table(struct task_args* targ) +{ + if (!targ->cpe_table) { + init_cpe4_table(targ); + } +} + +static inline void restore_cpe(struct cpe_pkt *packet, struct cpe_data *table, __attribute__((unused)) uint16_t qinq_tag, uint64_t *src_mac) +{ +#ifdef USE_QINQ + struct qinq_hdr *pqinq = &packet->qinq_hdr; + rte_memcpy(pqinq, &qinq_proto, sizeof(struct qinq_hdr)); + (*(uint64_t *)(&pqinq->d_addr)) = table->mac_port_8bytes; + /* set source as well now */ + *((uint64_t *)(&pqinq->s_addr)) = *((uint64_t *)&src_mac[table->mac_port.out_idx]); + pqinq->svlan.vlan_tci = table->qinq_svlan; + pqinq->cvlan.vlan_tci = table->qinq_cvlan; + pqinq->svlan.eth_proto = qinq_tag; + pqinq->cvlan.eth_proto = ETYPE_VLAN; + pqinq->ether_type = ETYPE_IPv4; +#else + (*(uint64_t *)(&packet->ether_hdr.d_addr)) = table->mac_port_8bytes; + /* set source as well now */ + *((uint64_t *)(&packet->ether_hdr.s_addr)) = *((uint64_t *)&src_mac[table->mac_port.out_idx]); + packet->ether_hdr.ether_type = ETYPE_IPv4; + + packet->ipv4_hdr.dst_addr = rte_bswap32(10 << 24 | rte_bswap16(table->qinq_svlan) << 12 | rte_bswap16(table->qinq_cvlan)); +#endif +} + +static inline uint8_t handle_qinq_encap4(struct task_qinq_encap4 *task, struct cpe_pkt *cpe_pkt, struct rte_mbuf *mbuf, struct cpe_data *entry); + +/* Same functionality as handle_qinq_encap_v4_bulk but untag MPLS as well. */ +static int handle_qinq_encap4_untag_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_encap4 *task = (struct task_qinq_encap4 *)tbase; + uint8_t out[MAX_PKT_BURST]; + prefetch_pkts(mbufs, n_pkts); + + for (uint16_t j = 0; j < n_pkts; ++j) { + if (likely(mpls_untag(mbufs[j]))) { + struct cpe_pkt* cpe_pkt = (struct cpe_pkt*) rte_pktmbuf_adj(mbufs[j], UPSTREAM_DELTA); + out[j] = handle_qinq_encap4(task, cpe_pkt, mbufs[j], NULL); + } + else { + out[j] = OUT_DISCARD; + } + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static inline void extract_key_bulk(struct task_qinq_encap4 *task, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + extract_key_core(mbufs[j], &task->keys[j]); + } +} + +__attribute__((cold)) static void handle_error(struct rte_mbuf *mbuf) +{ + struct core_net_pkt* core_pkt = rte_pktmbuf_mtod(mbuf, struct core_net_pkt *); + uint32_t dst_ip = core_pkt->ip_hdr.dst_addr; + uint32_t le_gre_id = rte_be_to_cpu_32(core_pkt->gre_hdr.gre_id); + + plogx_dbg("Unknown IP %x/gre_id %x\n", dst_ip, le_gre_id); +} + +static int handle_qinq_encap4_bulk_pe(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_encap4 *task = (struct task_qinq_encap4 *)tbase; + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + struct cpe_data* entries[64]; + uint8_t out[MAX_PKT_BURST]; + uint64_t lookup_hit_mask; + + prefetch_pkts(mbufs, n_pkts); + + for (uint16_t j = 0; j < n_pkts; ++j) { + struct ipv4_hdr* ip = (struct ipv4_hdr *)(rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *) + 1); + task->keys[j] = (uint64_t)ip->dst_addr; + } + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->cpe_table, task->fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (likely(lookup_hit_mask == pkts_mask)) { + for (uint16_t j = 0; j < n_pkts; ++j) { + struct cpe_pkt* cpe_pkt = (struct cpe_pkt*) rte_pktmbuf_prepend(mbufs[j], sizeof(struct qinq_hdr) - sizeof(struct ether_hdr)); + uint16_t padlen = mbuf_calc_padlen(mbufs[j], cpe_pkt, &cpe_pkt->ipv4_hdr); + + if (padlen) { + rte_pktmbuf_trim(mbufs[j], padlen); + } + out[j] = handle_qinq_encap4(task, cpe_pkt, mbufs[j], entries[j]); + } + } + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + handle_error(mbufs[j]); + out[j] = OUT_DISCARD; + continue; + } + struct cpe_pkt* cpe_pkt = (struct cpe_pkt*) rte_pktmbuf_prepend(mbufs[j], sizeof(struct qinq_hdr) - sizeof(struct ether_hdr)); + uint16_t padlen = mbuf_calc_padlen(mbufs[j], cpe_pkt, &cpe_pkt->ipv4_hdr); + + if (padlen) { + rte_pktmbuf_trim(mbufs[j], padlen); + } + out[j] = handle_qinq_encap4(task, cpe_pkt, mbufs[j], entries[j]); + } + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} +int handle_qinq_encap4_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_encap4 *task = (struct task_qinq_encap4 *)tbase; + uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t); + struct cpe_data* entries[64]; + uint8_t out[MAX_PKT_BURST]; + uint64_t lookup_hit_mask; + + prefetch_pkts(mbufs, n_pkts); + + // From GRE ID and IP address, retrieve QinQ and MAC addresses + extract_key_bulk(task, mbufs, n_pkts); + rte_table_hash_key8_ext_dosig_ops.f_lookup(task->cpe_table, task->fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (likely(lookup_hit_mask == pkts_mask)) { + for (uint16_t j = 0; j < n_pkts; ++j) { + struct cpe_pkt* cpe_pkt = (struct cpe_pkt*) rte_pktmbuf_adj(mbufs[j], UPSTREAM_DELTA); + // We are receiving GRE tunnelled packets (and removing UPSTRAM_DELTA bytes), whose length is > 64 bytes + // So there should be no padding, but in case the is one, remove it + uint16_t padlen = mbuf_calc_padlen(mbufs[j], cpe_pkt, &cpe_pkt->ipv4_hdr); + + if (padlen) { + rte_pktmbuf_trim(mbufs[j], padlen); + } + out[j] = handle_qinq_encap4(task, cpe_pkt, mbufs[j], entries[j]); + } + } + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(!((lookup_hit_mask >> j) & 0x1))) { + handle_error(mbufs[j]); + out[j] = OUT_DISCARD; + continue; + } + struct cpe_pkt* cpe_pkt = (struct cpe_pkt*) rte_pktmbuf_adj(mbufs[j], UPSTREAM_DELTA); + uint16_t padlen = mbuf_calc_padlen(mbufs[j], cpe_pkt, &cpe_pkt->ipv4_hdr); + + if (padlen) { + rte_pktmbuf_trim(mbufs[j], padlen); + } + out[j] = handle_qinq_encap4(task, cpe_pkt, mbufs[j], entries[j]); + } + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static inline uint8_t handle_qinq_encap4(struct task_qinq_encap4 *task, struct cpe_pkt *cpe_pkt, struct rte_mbuf *mbuf, struct cpe_data *entry) +{ + PROX_ASSERT(cpe_pkt); + + if (cpe_pkt->ipv4_hdr.time_to_live) { + cpe_pkt->ipv4_hdr.time_to_live--; + } + else { + plog_info("TTL = 0 => Dropping\n"); + return OUT_DISCARD; + } + cpe_pkt->ipv4_hdr.hdr_checksum = 0; + + restore_cpe(cpe_pkt, entry, task->qinq_tag, task->src_mac); + + if (task->runtime_flags & TASK_CLASSIFY) { + uint8_t queue = task->dscp[cpe_pkt->ipv4_hdr.type_of_service >> 2] & 0x3; + uint8_t tc = task->dscp[cpe_pkt->ipv4_hdr.type_of_service >> 2] >> 2; + + rte_sched_port_pkt_write(mbuf, 0, entry->user, tc, queue, 0); + } +#ifdef ENABLE_EXTRA_USER_STATISTICS + task->stats_per_user[entry->user]++; +#endif + if (task->runtime_flags & TASK_TX_CRC) { + prox_ip_cksum(mbuf, &cpe_pkt->ipv4_hdr, sizeof(struct qinq_hdr), sizeof(struct ipv4_hdr), task->offload_crc); + } + return entry->mac_port.out_idx; +} + +static void flow_iter_next(struct flow_iter *iter, struct task_args *targ) +{ + do { + iter->idx++; + uint8_t flag_features = iter->data; + + if (flag_features & TASK_FEATURE_LUT_QINQ_RSS) { + // If there is a load balancer, number of worker thread is indicated by targ->nb_slave_threads and n_rxq = 0 + // If there is no load balancers, number of worker thread is indicated by n_rxq and nb_slave_threads = 0 + uint8_t nb_worker_threads, worker_thread_id; + nb_worker_threads = 1; + worker_thread_id = 1; + if (targ->nb_slave_threads) { + nb_worker_threads = targ->nb_slave_threads; + worker_thread_id = targ->worker_thread_id; + } else if (prox_port_cfg[targ->rx_port_queue[0].port].n_rxq) { + nb_worker_threads = prox_port_cfg[targ->rx_port_queue[0].port].n_rxq; + worker_thread_id = targ->rx_port_queue[0].queue; + } else { + plog_err("Unexpected: unknown number of worker thread\n"); + } + + if (targ->nb_slave_threads == 0 || rss_to_queue(get_qinq_gre_map(targ)->entries[iter->idx].rss, nb_worker_threads) == worker_thread_id) + break; + } else if (flag_features & TASK_FEATURE_LUT_QINQ_HASH) { + uint64_t cvlan = rte_bswap16(get_qinq_gre_map(targ)->entries[iter->idx].cvlan & 0xFF0F); + uint64_t svlan = rte_bswap16(get_qinq_gre_map(targ)->entries[iter->idx].svlan & 0xFF0F); + uint64_t qinq = rte_bswap64((svlan << 32) | cvlan); + uint8_t queue = hash_crc32(&qinq, 8, 0) % targ->nb_slave_threads; + if (queue == targ->worker_thread_id) + break; + } else if (flag_features & TASK_FEATURE_GRE_ID) { + if (get_qinq_gre_map(targ)->entries[iter->idx].gre_id % targ->nb_slave_threads == targ->worker_thread_id) + break; + } + } while (iter->idx != (int)get_qinq_gre_map(targ)->count); +} + +static void flow_iter_beg(struct flow_iter *iter, struct task_args *targ) +{ + uint32_t flag_features = 0; + if (targ->lb_friend_core != 0xFF) { + struct task_args *lb_targ = &lcore_cfg[targ->lb_friend_core].targs[targ->lb_friend_task]; + flag_features = lb_targ->task_init->flag_features; + plog_info("\t\tWT %d Updated features to %x from friend %d\n", targ->lconf->id, flag_features, targ->lb_friend_core); + } else { + plog_info("\t\tWT %d has no friend\n", targ->lconf->id); + } + if (targ->nb_slave_threads == 0) { + // No slave threads, i.e. using RSS + plog_info("feature was %x is now %x\n", flag_features, TASK_FEATURE_LUT_QINQ_RSS); + flag_features = TASK_FEATURE_LUT_QINQ_RSS; + } + if ((flag_features & (TASK_FEATURE_GRE_ID|TASK_FEATURE_LUT_QINQ_RSS|TASK_FEATURE_LUT_QINQ_HASH)) == 0) { + plog_info("\t\tCould not find flag feature from Load balancer => supposing TASK_FEATURE_GRE_ID\n"); + flag_features = TASK_FEATURE_GRE_ID; + } + + iter->idx = -1; + flow_iter_next(iter, targ); +} + +static int flow_iter_is_end(struct flow_iter *iter, struct task_args *targ) +{ + return iter->idx == (int)get_qinq_gre_map(targ)->count; +} + +static uint32_t flow_iter_get_gre_id(struct flow_iter *iter, struct task_args *targ) +{ + return get_qinq_gre_map(targ)->entries[iter->idx].gre_id; +} + +static struct task_init task_init_qinq_encap4_table = { + .mode = QINQ_ENCAP4, + .mode_str = "qinqencapv4", + .early_init = early_init_table, + .init = init_task_qinq_encap4, + .handle = handle_qinq_encap4_bulk, + /* In this case user in qinq_lookup table is the QoS user + (from user_table), i.e. usually from 0 to 32K Otherwise it + would have been a user from (0 to n_interface x 32K) */ + .flow_iter = { + .beg = flow_iter_beg, + .is_end = flow_iter_is_end, + .next = flow_iter_next, + .get_gre_id = flow_iter_get_gre_id, + }, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_qinq_encap4) +}; + +static struct task_init task_init_qinq_encap4_table_pe = { + .mode = QINQ_ENCAP4, + .mode_str = "qinqencapv4", + .sub_mode_str = "pe", + .early_init = early_init_table, + .init = init_task_qinq_encap4, + .handle = handle_qinq_encap4_bulk_pe, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_qinq_encap4) +}; + +static struct task_init task_init_qinq_encap4_untag = { + .mode = QINQ_ENCAP4, + .sub_mode_str = "unmpls", + .mode_str = "qinqencapv4", + .init = init_task_qinq_encap4, + .handle = handle_qinq_encap4_untag_bulk, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_qinq_encap4) +}; + +__attribute__((constructor)) static void reg_task_qinq_encap4(void) +{ + reg_task(&task_init_qinq_encap4_table); + reg_task(&task_init_qinq_encap4_table_pe); + reg_task(&task_init_qinq_encap4_untag); +} diff --git a/VNFs/DPPD-PROX/handle_qinq_encap4.h b/VNFs/DPPD-PROX/handle_qinq_encap4.h new file mode 100644 index 00000000..639135e0 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_encap4.h @@ -0,0 +1,103 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_QINQ_ENCAP4_H_ +#define _HANDLE_QINQ_ENCAP4_H_ + +#include <rte_ip.h> +#include <rte_ether.h> + +#include "log.h" +#include "prox_assert.h" +#include "etypes.h" +#include "mpls.h" +#include "task_init.h" + +struct task_qinq_encap4 { + struct task_base base; + struct rte_table_hash *cpe_table; + uint16_t qinq_tag; + uint64_t src_mac[PROX_MAX_PORTS]; + int offload_crc; + uint8_t runtime_flags; + uint8_t *dscp; + uint64_t keys[64]; + struct rte_mbuf* fake_packets[64]; + uint64_t cpe_timeout; +#ifdef ENABLE_EXTRA_USER_STATISTICS + uint32_t *stats_per_user; + uint32_t n_users; +#endif +}; + +struct qinq_gre_entry { + uint16_t svlan; + uint16_t cvlan; + uint32_t gre_id; + uint32_t user; + uint32_t rss; // RSS based on Toeplitz_hash(svlan and cvlan) +}; + +struct qinq_gre_map { + uint32_t count; + struct qinq_gre_entry entries[0]; +}; + +struct qinq_gre_map *get_qinq_gre_map(struct task_args *targ); + +struct task_args; +struct prox_shared; + +void init_qinq_gre_table(struct task_args *targ, struct qinq_gre_map *qinq_gre_map); +void init_qinq_gre_hash(struct task_args *targ, struct qinq_gre_map *qinq_gre_map); +void init_cpe4_table(struct task_args *targ); +void init_cpe4_hash(struct task_args *targ); + +static inline uint8_t mpls_untag(struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + const uint16_t eth_type = peth->ether_type; + + if (eth_type == ETYPE_MPLSU) { + struct ether_hdr *pneweth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, 4); + const struct mpls_hdr *mpls = (const struct mpls_hdr *)(peth + 1); + + if (mpls->bos == 0) { + // Double MPLS tag + pneweth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, 4); + PROX_ASSERT(pneweth); + } + + const struct ipv4_hdr *pip = (const struct ipv4_hdr *)(pneweth + 1); + if ((pip->version_ihl >> 4) == 4) { + pneweth->ether_type = ETYPE_IPv4; + return 1; + } + else if ((pip->version_ihl >> 4) == 6) { + pneweth->ether_type = ETYPE_IPv6; + return 1; + } + + plog_info("Error removing MPLS: unexpected IP version: %d\n", pip->version_ihl >> 4); + return 0; + } + if (eth_type != ETYPE_LLDP) { + plog_info("Error Removing MPLS: ether_type = %#06x\n", eth_type); + } + return 0; +} + +#endif /* _HANDLE_QINQ_ENCAP4_H_ */ diff --git a/VNFs/DPPD-PROX/handle_qinq_encap6.c b/VNFs/DPPD-PROX/handle_qinq_encap6.c new file mode 100644 index 00000000..e5b774da --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_encap6.c @@ -0,0 +1,224 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_table_hash.h> + +#include "handle_qinq_encap6.h" +#include "handle_qinq_encap4.h" +#include "task_base.h" +#include "qinq.h" +#include "defines.h" +#include "tx_pkt.h" +#include "hash_entry_types.h" +#include "prefetch.h" +#include "log.h" +#include "lconf.h" +#include "mpls.h" +#include "hash_utils.h" +#include "quit.h" + +struct task_qinq_encap6 { + struct task_base base; + uint16_t qinq_tag; + uint8_t tx_portid; + uint8_t runtime_flags; + struct rte_table_hash *cpe_table; +}; + +static void init_task_qinq_encap6(struct task_base *tbase, struct task_args *targ) +{ + struct task_qinq_encap6 *task = (struct task_qinq_encap6 *)tbase; + + task->qinq_tag = targ->qinq_tag; + task->cpe_table = targ->cpe_table; + task->runtime_flags = targ->runtime_flags; +} + +/* Encapsulate IPv6 packet in QinQ where the QinQ is derived from the IPv6 address */ +static inline uint8_t handle_qinq_encap6(struct rte_mbuf *mbuf, struct task_qinq_encap6 *task) +{ + struct qinq_hdr *pqinq = (struct qinq_hdr *)rte_pktmbuf_prepend(mbuf, 2 * sizeof(struct vlan_hdr)); + + PROX_ASSERT(pqinq); + struct ipv6_hdr *pip6 = (struct ipv6_hdr *)(pqinq + 1); + + if (pip6->hop_limits) { + pip6->hop_limits--; + } + else { + plog_info("TTL = 0 => Dropping\n"); + return OUT_DISCARD; + } + + // TODO: optimize to use bulk as intended with the rte_table_library + uint64_t pkts_mask = RTE_LEN2MASK(1, uint64_t); + uint64_t lookup_hit_mask; + struct cpe_data* entries[64]; // TODO: use bulk size + rte_table_hash_ext_dosig_ops.f_lookup(task->cpe_table, &mbuf, pkts_mask, &lookup_hit_mask, (void**)entries); + + if (lookup_hit_mask == 0x1) { + /* will also overwrite part of the destination addr */ + (*(uint64_t *)pqinq) = entries[0]->mac_port_8bytes; + pqinq->svlan.eth_proto = task->qinq_tag; + pqinq->cvlan.eth_proto = ETYPE_VLAN; + pqinq->svlan.vlan_tci = entries[0]->qinq_svlan; + pqinq->cvlan.vlan_tci = entries[0]->qinq_cvlan; + pqinq->ether_type = ETYPE_IPv6; + + /* classification can only be done from this point */ + if (task->runtime_flags & TASK_CLASSIFY) { + rte_sched_port_pkt_write(mbuf, 0, entries[0]->user, 0, 0, 0); + } + return 0; + } + else { + plogx_err("Unknown IP " IPv6_BYTES_FMT "\n", IPv6_BYTES(pip6->dst_addr)); + return OUT_DISCARD; + } +} + +void init_cpe6_table(struct task_args *targ) +{ + char name[64]; + sprintf(name, "core_%u_CPEv6Table", targ->lconf->id); + + uint8_t table_part = targ->nb_slave_threads; + if (!rte_is_power_of_2(table_part)) { + table_part = rte_align32pow2(table_part) >> 1; + } + + uint32_t n_entries = MAX_GRE / table_part; + struct rte_table_hash_ext_params table_hash_params = { + .key_size = sizeof(struct ipv6_addr), + .n_keys = n_entries, + .n_buckets = n_entries >> 2, + .n_buckets_ext = n_entries >> 3, + .f_hash = hash_crc32, + .seed = 0, + .signature_offset = HASH_METADATA_OFFSET(0), + .key_offset = HASH_METADATA_OFFSET(0), + }; + + size_t entry_size = sizeof(struct cpe_data); + if (!rte_is_power_of_2(entry_size)) { + entry_size = rte_align32pow2(entry_size); + } + + struct rte_table_hash* phash = rte_table_hash_ext_dosig_ops. + f_create(&table_hash_params, rte_lcore_to_socket_id(targ->lconf->id), entry_size); + PROX_PANIC(phash == NULL, "Unable to allocate memory for IPv6 hash table on core %u\n", targ->lconf->id); + + for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) { + enum task_mode smode = targ->lconf->targs[task_id].mode; + if (smode == QINQ_DECAP6 || smode == QINQ_ENCAP6) { + targ->lconf->targs[task_id].cpe_table = phash; + } + } +} + +static void early_init(struct task_args *targ) +{ + if (!targ->cpe_table) { + init_cpe6_table(targ); + } +} + +static int handle_qinq_encap6_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_encap6 *task = (struct task_qinq_encap6 *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_qinq_encap6(mbufs[j], task); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_qinq_encap6(mbufs[j], task); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static int handle_qinq_encap6_untag_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qinq_encap6 *task = (struct task_qinq_encap6 *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + if (likely(mpls_untag(mbufs[j]))) { + out[j] = handle_qinq_encap6(mbufs[j], task); + } + else { + out[j] = OUT_DISCARD; + } + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + if (likely(mpls_untag(mbufs[j]))) { + out[j] = handle_qinq_encap6(mbufs[j], task); + } + else { + out[j] = OUT_DISCARD; + } + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static struct task_init task_init_qinq_encap6 = { + .mode = QINQ_ENCAP6, + .mode_str = "qinqencapv6", + .init = init_task_qinq_encap6, + .early_init = early_init, + .handle = handle_qinq_encap6_bulk, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_qinq_encap6) +}; + +static struct task_init task_init_qinq_encap6_untag = { + .mode = QINQ_ENCAP6, + .mode_str = "qinqencapv6", + .sub_mode_str = "unmpls", + .early_init = early_init, + .init = init_task_qinq_encap6, + .handle = handle_qinq_encap6_untag_bulk, + .flag_features = TASK_FEATURE_CLASSIFY, + .size = sizeof(struct task_qinq_encap6) +}; + +__attribute__((constructor)) static void reg_task_qinq_encap6(void) +{ + reg_task(&task_init_qinq_encap6); + reg_task(&task_init_qinq_encap6_untag); +} diff --git a/VNFs/DPPD-PROX/handle_qinq_encap6.h b/VNFs/DPPD-PROX/handle_qinq_encap6.h new file mode 100644 index 00000000..1f72b53c --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qinq_encap6.h @@ -0,0 +1,24 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_QINQ_ENCAP6_H_ +#define _HANDLE_QINQ_ENCAP6_H_ + +struct task_args; + +void init_cpe6_table(struct task_args *targ); + +#endif /* _HANDLE_QINQ_ENCAP6_H_ */ diff --git a/VNFs/DPPD-PROX/handle_qos.c b/VNFs/DPPD-PROX/handle_qos.c new file mode 100644 index 00000000..eef64796 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qos.c @@ -0,0 +1,179 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> +#include <rte_mbuf.h> +#include <rte_sched.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "etypes.h" +#include "stats.h" +#include "task_init.h" +#include "lconf.h" +#include "task_base.h" +#include "defines.h" +#include "prefetch.h" +#include "handle_qos.h" +#include "log.h" +#include "quit.h" +#include "qinq.h" +#include "prox_cfg.h" +#include "prox_shared.h" + +struct task_qos { + struct task_base base; + struct rte_sched_port *sched_port; + uint16_t *user_table; + uint8_t *dscp; + uint32_t nb_buffered_pkts; + uint8_t runtime_flags; +}; + +uint32_t task_qos_n_pkts_buffered(struct task_base *tbase) +{ + struct task_qos *task = (struct task_qos *)tbase; + + return task->nb_buffered_pkts; +} + +static inline int handle_qos_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_qos *task = (struct task_qos *)tbase; + int ret = 0; + + if (n_pkts) { + if (task->runtime_flags & TASK_CLASSIFY) { + uint16_t j; +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + prefetch_nta(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + uint8_t queue = 0; + uint8_t tc = 0; + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { + prefetch_nta(mbufs[j + PREFETCH_OFFSET]); + prefetch_nta(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); + const struct qinq_hdr *pqinq = rte_pktmbuf_mtod(mbufs[j], const struct qinq_hdr *); + uint32_t qinq = PKT_TO_LUTQINQ(pqinq->svlan.vlan_tci, pqinq->cvlan.vlan_tci); + if (pqinq->ether_type == ETYPE_IPv4) { + const struct ipv4_hdr *ipv4_hdr = (const struct ipv4_hdr *)(pqinq + 1); + queue = task->dscp[ipv4_hdr->type_of_service >> 2] & 0x3; + tc = task->dscp[ipv4_hdr->type_of_service >> 2] >> 2; + } else { + // Keep queue and tc = 0 for other packet types like ARP + queue = 0; + tc = 0; + } + + rte_sched_port_pkt_write(mbufs[j], 0, task->user_table[qinq], tc, queue, 0); + } +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + const struct qinq_hdr *pqinq = rte_pktmbuf_mtod(mbufs[j], const struct qinq_hdr *); + uint32_t qinq = PKT_TO_LUTQINQ(pqinq->svlan.vlan_tci, pqinq->cvlan.vlan_tci); + if (pqinq->ether_type == ETYPE_IPv4) { + const struct ipv4_hdr *ipv4_hdr = (const struct ipv4_hdr *)(pqinq + 1); + queue = task->dscp[ipv4_hdr->type_of_service >> 2] & 0x3; + tc = task->dscp[ipv4_hdr->type_of_service >> 2] >> 2; + } else { + // Keep queue and tc = 0 for other packet types like ARP + queue = 0; + tc = 0; + } + + rte_sched_port_pkt_write(mbufs[j], 0, task->user_table[qinq], tc, queue, 0); + } +#endif + } + int16_t ret = rte_sched_port_enqueue(task->sched_port, mbufs, n_pkts); + task->nb_buffered_pkts += ret; + TASK_STATS_ADD_IDLE(&task->base.aux->stats, n_pkts - ret); + } + + if (task->nb_buffered_pkts) { + n_pkts = rte_sched_port_dequeue(task->sched_port, mbufs, 32); + if (likely(n_pkts)) { + task->nb_buffered_pkts -= n_pkts; + ret = task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); + } + } + return ret; +} + +static void init_task_qos(struct task_base *tbase, struct task_args *targ) +{ + struct task_qos *task = (struct task_qos *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + char name[64]; + + snprintf(name, sizeof(name), "qos_sched_port_%u_%u", targ->lconf->id, 0); + + targ->qos_conf.port_params.name = name; + targ->qos_conf.port_params.socket = socket_id; + task->sched_port = rte_sched_port_config(&targ->qos_conf.port_params); + + PROX_PANIC(task->sched_port == NULL, "failed to create sched_port"); + + plog_info("number of pipes: %d\n\n", targ->qos_conf.port_params.n_pipes_per_subport); + int err = rte_sched_subport_config(task->sched_port, 0, targ->qos_conf.subport_params); + PROX_PANIC(err != 0, "Failed setting up sched_port subport, error: %d", err); + + /* only single subport and single pipe profile is supported */ + for (uint32_t pipe = 0; pipe < targ->qos_conf.port_params.n_pipes_per_subport; ++pipe) { + err = rte_sched_pipe_config(task->sched_port, 0 , pipe, 0); + PROX_PANIC(err != 0, "failed setting up sched port pipe, error: %d", err); + } + + task->runtime_flags = targ->runtime_flags; + + task->user_table = prox_sh_find_socket(socket_id, "user_table"); + if (!task->user_table) { + PROX_PANIC(!strcmp(targ->user_table, ""), "No user table defined\n"); + int ret = lua_to_user_table(prox_lua(), GLOBAL, targ->user_table, socket_id, &task->user_table); + PROX_PANIC(ret, "Failed to create user table from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, "user_table", task->user_table); + } + + if (task->runtime_flags & TASK_CLASSIFY) { + PROX_PANIC(!strcmp(targ->dscp, ""), "DSCP table not specified\n"); + task->dscp = prox_sh_find_socket(socket_id, targ->dscp); + if (!task->dscp) { + int ret = lua_to_dscp(prox_lua(), GLOBAL, targ->dscp, socket_id, &task->dscp); + PROX_PANIC(ret, "Failed to create dscp table from config:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->dscp, task->dscp); + } + } +} + +static struct task_init task_init_qos = { + .mode_str = "qos", + .init = init_task_qos, + .handle = handle_qos_bulk, + .flag_features = TASK_FEATURE_CLASSIFY | TASK_FEATURE_NEVER_DISCARDS | TASK_FEATURE_MULTI_RX | TASK_FEATURE_ZERO_RX, + .size = sizeof(struct task_qos) +}; + +__attribute__((constructor)) static void reg_task_qos(void) +{ + reg_task(&task_init_qos); +} diff --git a/VNFs/DPPD-PROX/handle_qos.h b/VNFs/DPPD-PROX/handle_qos.h new file mode 100644 index 00000000..0fabe9b8 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_qos.h @@ -0,0 +1,26 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_QOS_H_ +#define _HANDLE_QOS_H_ + +#include <inttypes.h> + +struct task_base; + +uint32_t task_qos_n_pkts_buffered(struct task_base *tbase); + +#endif /* _HANDLE_QOS_H_ */ diff --git a/VNFs/DPPD-PROX/handle_read.c b/VNFs/DPPD-PROX/handle_read.c new file mode 100644 index 00000000..9a06a2b1 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_read.c @@ -0,0 +1,78 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> + +#include "task_base.h" +#include "task_init.h" +#include "defines.h" +#include "prefetch.h" +#include "log.h" + +struct task_read { + struct task_base base; +}; + +static int handle_read_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_read *task = (struct task_read *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + uint64_t *first; + +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + first = rte_pktmbuf_mtod(mbufs[j], uint64_t *); + out[j] = *first != 0? 0: OUT_DISCARD; + } +#ifdef PROX_PREFETCH_OFFSET + prefetch_nta(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + first = rte_pktmbuf_mtod(mbufs[j], uint64_t *); + out[j] = *first != 0? 0: OUT_DISCARD; + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void init_task_read(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct task_args *targ) +{ +} + +static struct task_init task_init_read = { + .mode_str = "read", + .init = init_task_read, + .handle = handle_read_bulk, + .size = sizeof(struct task_read) +}; + +__attribute__((constructor)) static void reg_task_read(void) +{ + reg_task(&task_init_read); +} diff --git a/VNFs/DPPD-PROX/handle_routing.c b/VNFs/DPPD-PROX/handle_routing.c new file mode 100644 index 00000000..9dd45ed8 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_routing.c @@ -0,0 +1,321 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_lpm.h> +#include <rte_cycles.h> +#include <string.h> +#include <rte_version.h> +#include <rte_ip.h> +#include <rte_byteorder.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "quit.h" +#include "log.h" +#include "handle_routing.h" +#include "tx_pkt.h" +#include "gre.h" +#include "lconf.h" +#include "prox_port_cfg.h" +#include "etypes.h" +#include "prefetch.h" +#include "hash_entry_types.h" +#include "mpls.h" +#include "qinq.h" +#include "prox_cfg.h" +#include "ip6_addr.h" +#include "prox_shared.h" +#include "prox_cksum.h" +#include "mbuf_utils.h" + +struct task_routing { + struct task_base base; + uint8_t runtime_flags; + struct lcore_cfg *lconf; + struct rte_lpm *ipv4_lpm; + struct next_hop *next_hops; + int offload_crc; + uint32_t number_free_rules; + uint16_t qinq_tag; + uint32_t marking[4]; + uint64_t src_mac[PROX_MAX_PORTS]; +}; + +static void routing_update(struct task_base *tbase, void **data, uint16_t n_msgs) +{ + struct task_routing *task = (struct task_routing *)tbase; + struct route_msg *msg; + + for (uint16_t i = 0; i < n_msgs; ++i) { + msg = (struct route_msg *)data[i]; + + if (task->number_free_rules == 0) { + plog_warn("Failed adding route: %u.%u.%u.%u/%u: lpm table full\n", + msg->ip_bytes[0], msg->ip_bytes[1], msg->ip_bytes[2], + msg->ip_bytes[3], msg->prefix); + } else { + if (rte_lpm_add(task->ipv4_lpm, rte_bswap32(msg->ip), msg->prefix, msg->nh)) { + plog_warn("Failed adding route: %u.%u.%u.%u/%u\n", + msg->ip_bytes[0], msg->ip_bytes[1], msg->ip_bytes[2], + msg->ip_bytes[3], msg->prefix); + } else { + task->number_free_rules--; + } + } + } +} + +static void init_task_routing(struct task_base *tbase, struct task_args *targ) +{ + struct task_routing *task = (struct task_routing *)tbase; + const int socket_id = rte_lcore_to_socket_id(targ->lconf->id); + struct lpm4 *lpm; + + task->lconf = targ->lconf; + task->qinq_tag = targ->qinq_tag; + task->runtime_flags = targ->runtime_flags; + + PROX_PANIC(!strcmp(targ->route_table, ""), "route table not specified\n"); + if (targ->flags & TASK_ARG_LOCAL_LPM) { + int ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm); + PROX_PANIC(ret, "Failed to load IPv4 LPM:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->route_table, lpm); + + task->number_free_rules = lpm->n_free_rules; + } + else { + lpm = prox_sh_find_socket(socket_id, targ->route_table); + if (!lpm) { + int ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm); + PROX_PANIC(ret, "Failed to load IPv4 LPM:\n%s\n", get_lua_to_errors()); + prox_sh_add_socket(socket_id, targ->route_table, lpm); + } + } + task->ipv4_lpm = lpm->rte_lpm; + task->next_hops = lpm->next_hops; + task->number_free_rules = lpm->n_free_rules; + + for (uint32_t i = 0; i < MAX_HOP_INDEX; i++) { + int tx_port = task->next_hops[i].mac_port.out_idx; + if ((tx_port > targ->nb_txports - 1) && (tx_port > targ->nb_txrings - 1)) { + PROX_PANIC(1, "Routing Table contains port %d but only %d tx port/ %d ring:\n", tx_port, targ->nb_txports, targ->nb_txrings); + } + } + + if (targ->nb_txrings) { + struct task_args *dtarg; + struct core_task ct; + for (uint32_t i = 0; i < targ->nb_txrings; ++i) { + ct = targ->core_task_set[0].core_task[i]; + dtarg = core_targ_get(ct.core, ct.task); + dtarg = find_reachable_task_sending_to_port(dtarg); + if (task->runtime_flags & TASK_MPLS_TAGGING) { + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[dtarg->tx_port_queue[0].port].eth_addr))) | ((uint64_t)ETYPE_MPLSU << (64 - 16)); + } else { + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[dtarg->tx_port_queue[0].port].eth_addr))) | ((uint64_t)ETYPE_IPv4 << (64 - 16)); + } + } + } else { + for (uint32_t i = 0; i < targ->nb_txports; ++i) { + if (task->runtime_flags & TASK_MPLS_TAGGING) { + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[targ->tx_port_queue[i].port].eth_addr))) | ((uint64_t)ETYPE_MPLSU << (64 - 16)); + } else { + task->src_mac[i] = (0x0000ffffffffffff & ((*(uint64_t*)&prox_port_cfg[targ->tx_port_queue[i].port].eth_addr))) | ((uint64_t)ETYPE_IPv4 << (64 - 16)); + } + } + } + + for (uint32_t i = 0; i < 4; ++i) { + task->marking[i] = rte_bswap32(targ->marking[i] << 9); + } + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->capabilities.tx_offload_cksum; + } + + targ->lconf->ctrl_func_m[targ->task] = routing_update; + targ->lconf->ctrl_timeout = freq_to_tsc(20); +} + +static inline uint8_t handle_routing(struct task_routing *task, struct rte_mbuf *mbuf); + +static int handle_routing_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_routing *task = (struct task_routing *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_routing(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_routing(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void set_l2(struct task_routing *task, struct rte_mbuf *mbuf, uint8_t nh_idx) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + *((uint64_t *)(&peth->d_addr)) = task->next_hops[nh_idx].mac_port_8bytes; + *((uint64_t *)(&peth->s_addr)) = task->src_mac[task->next_hops[nh_idx].mac_port.out_idx]; +} + +static void set_l2_mpls(struct task_routing *task, struct rte_mbuf *mbuf, uint8_t nh_idx, uint16_t l2_len) +{ + struct ether_hdr *peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, sizeof(struct mpls_hdr)); + l2_len += sizeof(struct mpls_hdr); + prox_ip_cksum(mbuf, (struct ipv4_hdr *)((uint8_t *)peth + l2_len), l2_len, sizeof(struct ipv4_hdr), task->offload_crc); + + *((uint64_t *)(&peth->d_addr)) = task->next_hops[nh_idx].mac_port_8bytes; + *((uint64_t *)(&peth->s_addr)) = task->src_mac[task->next_hops[nh_idx].mac_port.out_idx]; + /* MPLSU ether_type written as high word of 64bit src_mac prepared by init_task_routing */ + struct mpls_hdr *mpls = (struct mpls_hdr *)(peth + 1); + + if (task->runtime_flags & TASK_MARK) { + enum rte_meter_color color = rte_sched_port_pkt_read_color(mbuf); + + *(uint32_t *)mpls = task->next_hops[nh_idx].mpls | task->marking[color] | 0x00010000; // Set BoS to 1 + } + else { + *(uint32_t *)mpls = task->next_hops[nh_idx].mpls | 0x00010000; // Set BoS to 1 + } +} + +static uint8_t route_ipv4(struct task_routing *task, uint8_t *beg, uint32_t ip_offset, struct rte_mbuf *mbuf) +{ + struct ipv4_hdr *ip = (struct ipv4_hdr*)(beg + ip_offset); + struct ether_hdr *peth_out; + uint8_t tx_port; + uint32_t dst_ip; + + if (unlikely(ip->version_ihl >> 4 != 4)) { + plog_warn("Offset: %d\n", ip_offset); + plog_warn("Expected to receive IPv4 packet but IP version was %d\n", + ip->version_ihl >> 4); + return OUT_DISCARD; + } + + switch(ip->next_proto_id) { + case IPPROTO_GRE: { + struct gre_hdr *pgre = (struct gre_hdr *)(ip + 1); + dst_ip = ((struct ipv4_hdr *)(pgre + 1))->dst_addr; + break; + } + case IPPROTO_TCP: + case IPPROTO_UDP: + dst_ip = ip->dst_addr; + break; + default: + /* Routing for other protocols is not implemented */ + return OUT_DISCARD; + } + +#if RTE_VERSION >= RTE_VERSION_NUM(16,4,0,1) + uint32_t next_hop_index; +#else + uint8_t next_hop_index; +#endif + if (unlikely(rte_lpm_lookup(task->ipv4_lpm, rte_bswap32(dst_ip), &next_hop_index) != 0)) { + uint8_t* dst_ipp = (uint8_t*)&dst_ip; + plog_warn("lpm_lookup failed for ip %d.%d.%d.%d: rc = %d\n", + dst_ipp[0], dst_ipp[1], dst_ipp[2], dst_ipp[3], -ENOENT); + return OUT_DISCARD; + } + + tx_port = task->next_hops[next_hop_index].mac_port.out_idx; + if (task->runtime_flags & TASK_MPLS_TAGGING) { + uint16_t padlen = rte_pktmbuf_pkt_len(mbuf) - rte_be_to_cpu_16(ip->total_length) - ip_offset; + if (padlen) { + rte_pktmbuf_trim(mbuf, padlen); + } + + set_l2_mpls(task, mbuf, next_hop_index, ip_offset); + } + else { + set_l2(task, mbuf, next_hop_index); + } + return tx_port; +} + +static inline uint8_t handle_routing(struct task_routing *task, struct rte_mbuf *mbuf) +{ + struct qinq_hdr *qinq; + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + + switch (peth->ether_type) { + case ETYPE_8021ad: { + struct qinq_hdr *qinq = (struct qinq_hdr *)peth; + if ((qinq->cvlan.eth_proto != ETYPE_VLAN)) { + plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto); + return OUT_DISCARD; + } + + return route_ipv4(task, (uint8_t*)qinq, sizeof(*qinq), mbuf); + } + case ETYPE_IPv4: + return route_ipv4(task, (uint8_t*)peth, sizeof(*peth), mbuf); + case ETYPE_MPLSU: { + /* skip MPLS headers if any for routing */ + struct mpls_hdr *mpls = (struct mpls_hdr *)(peth + 1); + uint32_t count = sizeof(struct ether_hdr); + while (!(mpls->bytes & 0x00010000)) { + mpls++; + count += sizeof(struct mpls_hdr); + } + count += sizeof(struct mpls_hdr); + + return route_ipv4(task, (uint8_t*)peth, count, mbuf); + } + default: + if (peth->ether_type == task->qinq_tag) { + struct qinq_hdr *qinq = (struct qinq_hdr *)peth; + if ((qinq->cvlan.eth_proto != ETYPE_VLAN)) { + plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto); + return OUT_DISCARD; + } + + return route_ipv4(task, (uint8_t*)qinq, sizeof(*qinq), mbuf); + } + plog_warn("Failed routing packet: ether_type %#06x is unknown\n", peth->ether_type); + return OUT_DISCARD; + } +} + +static struct task_init task_init_routing = { + .mode_str = "routing", + .init = init_task_routing, + .handle = handle_routing_bulk, + .flag_features = TASK_FEATURE_ROUTING, + .size = sizeof(struct task_routing) +}; + +__attribute__((constructor)) static void reg_task_routing(void) +{ + reg_task(&task_init_routing); +} diff --git a/VNFs/DPPD-PROX/handle_routing.h b/VNFs/DPPD-PROX/handle_routing.h new file mode 100644 index 00000000..e3dde93d --- /dev/null +++ b/VNFs/DPPD-PROX/handle_routing.h @@ -0,0 +1,29 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_ROUTING_H_ +#define _HANDLE_ROUTING_H_ + +struct route_msg { + union { + uint32_t ip; + uint8_t ip_bytes[4]; + }; + uint32_t prefix; + uint32_t nh; +}; + +#endif /* _HANDLE_ROUTING_H_ */ diff --git a/VNFs/DPPD-PROX/handle_swap.c b/VNFs/DPPD-PROX/handle_swap.c new file mode 100644 index 00000000..8e5a94ce --- /dev/null +++ b/VNFs/DPPD-PROX/handle_swap.c @@ -0,0 +1,291 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_udp.h> + +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "log.h" +#include "arp.h" +#include "handle_swap.h" +#include "prox_port_cfg.h" +#include "mpls.h" +#include "qinq.h" +#include "gre.h" +#include "prefetch.h" + +struct task_swap { + struct task_base base; + uint8_t src_dst_mac[12]; + uint32_t runtime_flags; + uint32_t tmp_ip; + uint32_t ip; +}; + +static void task_update_config(struct task_swap *task) +{ + if (unlikely(task->ip != task->tmp_ip)) + task->ip = task->tmp_ip; +} + +static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *hdr; + struct ether_addr mac; + + if (unlikely((task->runtime_flags & (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET)) == (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET))) { + /* Source and Destination mac hardcoded */ + hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + rte_memcpy(hdr, task->src_dst_mac, sizeof(task->src_dst_mac)); + } else { + hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + if (likely((task->runtime_flags & TASK_ARG_SRC_MAC_SET) == 0)) { + /* dst mac will be used as src mac */ + ether_addr_copy(&hdr->d_addr, &mac); + } + + if (unlikely(task->runtime_flags & TASK_ARG_DST_MAC_SET)) + ether_addr_copy((struct ether_addr *)&task->src_dst_mac[0], &hdr->d_addr); + else + ether_addr_copy(&hdr->s_addr, &hdr->d_addr); + + if (unlikely(task->runtime_flags & TASK_ARG_SRC_MAC_SET)) { + ether_addr_copy((struct ether_addr *)&task->src_dst_mac[6], &hdr->s_addr); + } else { + ether_addr_copy(&mac, &hdr->s_addr); + } + } +} +static inline int handle_arp_request(struct task_swap *task, struct ether_hdr_arp *hdr_arp, struct ether_addr *s_addr, uint32_t ip) +{ + if ((hdr_arp->arp.data.tpa == ip) || (ip == 0)) { + prepare_arp_reply(hdr_arp, s_addr); + memcpy(hdr_arp->ether_hdr.d_addr.addr_bytes, hdr_arp->ether_hdr.s_addr.addr_bytes, 6); + memcpy(hdr_arp->ether_hdr.s_addr.addr_bytes, s_addr, 6); + return 0; + } else if (task->runtime_flags & TASK_MULTIPLE_MAC) { + struct ether_addr tmp_s_addr; + create_mac(hdr_arp, &tmp_s_addr); + prepare_arp_reply(hdr_arp, &tmp_s_addr); + memcpy(hdr_arp->ether_hdr.d_addr.addr_bytes, hdr_arp->ether_hdr.s_addr.addr_bytes, 6); + memcpy(hdr_arp->ether_hdr.s_addr.addr_bytes, &tmp_s_addr, 6); + return 0; + } else { + plogx_dbg("Received ARP on unexpected IP %x, expecting %x\n", rte_be_to_cpu_32(hdr_arp->arp.data.tpa), rte_be_to_cpu_32(ip)); + return OUT_DISCARD; + } +} + +/* + * swap mode does not send arp requests, so does not expect arp replies + * Need to understand later whether we must send arp requests + */ +static inline int handle_arp_replies(struct task_swap *task, struct ether_hdr_arp *hdr_arp) +{ + return OUT_DISCARD; +} + +static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_swap *task = (struct task_swap *)tbase; + struct ether_hdr *hdr; + struct ether_addr mac; + struct ipv4_hdr *ip_hdr; + struct udp_hdr *udp_hdr; + uint32_t ip; + uint16_t port; + uint8_t out[64] = {0}; + struct mpls_hdr *mpls; + uint32_t mpls_len = 0; + struct qinq_hdr *qinq; + struct vlan_hdr *vlan; + struct ether_hdr_arp *hdr_arp; + uint16_t j; + + for (j = 0; j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 0; j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void *)); + } + + for (uint16_t j = 0; j < n_pkts; ++j) { + hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *); + switch (hdr->ether_type) { + case ETYPE_ARP: + hdr_arp = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr_arp *); + if (arp_is_gratuitous(hdr_arp)) { + plog_info("Received gratuitous packet \n"); + out[j] = OUT_DISCARD; + } else if (hdr_arp->arp.oper == ARP_REQUEST) { + out[j] = handle_arp_request(task, hdr_arp, (struct ether_addr *)&task->src_dst_mac[6], task->ip); + } else if (hdr_arp->arp.oper == ARP_REPLY) { + out[j] = handle_arp_replies(task, hdr_arp); + } else { + plog_info("Received unexpected ARP operation %d\n", hdr_arp->arp.oper); + out[j] = OUT_DISCARD; + } + continue; + case ETYPE_MPLSU: + mpls = (struct mpls_hdr *)(hdr + 1); + while (!(mpls->bytes & 0x00010000)) { + mpls++; + mpls_len += sizeof(struct mpls_hdr); + } + mpls_len += sizeof(struct mpls_hdr); + ip_hdr = (struct ipv4_hdr *)(mpls + 1); + break; + case ETYPE_8021ad: + qinq = (struct qinq_hdr *)hdr; + if (qinq->cvlan.eth_proto != ETYPE_VLAN) { + plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto); + out[j] = OUT_DISCARD; + continue; + } + ip_hdr = (struct ipv4_hdr *)(qinq + 1); + break; + case ETYPE_VLAN: + vlan = (struct vlan_hdr *)(hdr + 1); + if (vlan->eth_proto == ETYPE_IPv4) { + ip_hdr = (struct ipv4_hdr *)(vlan + 1); + } else if (vlan->eth_proto == ETYPE_VLAN) { + vlan = (struct vlan_hdr *)(vlan + 1); + if (vlan->eth_proto == ETYPE_IPv4) { + ip_hdr = (struct ipv4_hdr *)(vlan + 1); + } + else if (vlan->eth_proto == ETYPE_IPv6) { + plog_warn("Unsupported IPv6\n"); + out[j] = OUT_DISCARD; + continue; + } + else { + plog_warn("Unsupported packet type\n"); + out[j] = OUT_DISCARD; + continue; + } + } else { + plog_warn("Unsupported packet type\n"); + out[j] = OUT_DISCARD; + continue; + } + break; + case ETYPE_IPv4: + ip_hdr = (struct ipv4_hdr *)(hdr + 1); + break; + case ETYPE_IPv6: + plog_warn("Unsupported IPv6\n"); + out[j] = OUT_DISCARD; + continue; + case ETYPE_LLDP: + out[j] = OUT_DISCARD; + continue; + default: + plog_warn("Unsupported ether_type 0x%x\n", hdr->ether_type); + out[j] = OUT_DISCARD; + continue; + } + udp_hdr = (struct udp_hdr *)(ip_hdr + 1); + ip = ip_hdr->dst_addr; + ip_hdr->dst_addr = ip_hdr->src_addr; + ip_hdr->src_addr = ip; + if (ip_hdr->next_proto_id == IPPROTO_GRE) { + struct gre_hdr *pgre = (struct gre_hdr *)(ip_hdr + 1); + struct ipv4_hdr *inner_ip_hdr = ((struct ipv4_hdr *)(pgre + 1)); + ip = inner_ip_hdr->dst_addr; + inner_ip_hdr->dst_addr = inner_ip_hdr->src_addr; + inner_ip_hdr->src_addr = ip; + udp_hdr = (struct udp_hdr *)(inner_ip_hdr + 1); + port = udp_hdr->dst_port; + udp_hdr->dst_port = udp_hdr->src_port; + udp_hdr->src_port = port; + } else { + port = udp_hdr->dst_port; + udp_hdr->dst_port = udp_hdr->src_port; + udp_hdr->src_port = port; + } + write_src_and_dst_mac(task, mbufs[j]); + } + task_update_config(task); + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void init_task_swap(struct task_base *tbase, struct task_args *targ) +{ + struct task_swap *task = (struct task_swap *)tbase; + struct ether_addr *src_addr, *dst_addr; + + /* + * Destination MAC can come from + * - pre-configured mac in case 'dst mac=xx:xx:xx:xx:xx:xx' in config file + * - src mac from the packet in case 'dst mac=packet' in config file + * - not written in case 'dst mac=no' in config file + * - (default - no 'dst mac') src mac from the packet + * Source MAC can come from + * - pre-configured mac in case 'src mac=xx:xx:xx:xx:xx:xx' in config file + * - dst mac from the packet in case 'src mac=packet' in config file + * - not written in case 'src mac=no' in config file + * - (default - no 'src mac') if (tx_port) port mac + * - (default - no 'src mac') if (no tx_port) dst mac from the packet + */ + + if (targ->flags & TASK_ARG_DST_MAC_SET) { + dst_addr = &targ->edaddr; + memcpy(&task->src_dst_mac[0], dst_addr, sizeof(*src_addr)); + } + + if (targ->flags & TASK_ARG_SRC_MAC_SET) { + src_addr = &targ->esaddr; + memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr)); + plog_info("\t\tCore %d: src mac set from config file\n", targ->lconf->id); + } else if (targ->nb_txports) { + src_addr = &prox_port_cfg[task->base.tx_params_hw.tx_port_queue[0].port].eth_addr; + memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr)); + if (targ->flags & TASK_ARG_HW_SRC_MAC){ + targ->flags |= TASK_ARG_SRC_MAC_SET; + plog_info("\t\tCore %d: src mac set from port\n", targ->lconf->id); + } + } + task->runtime_flags = targ->flags; + task->ip = rte_cpu_to_be_32(targ->local_ipv4); + task->tmp_ip = task->ip; +} + +static struct task_init task_init_swap = { + .mode_str = "swap", + .init = init_task_swap, + .handle = handle_swap_bulk, + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .size = sizeof(struct task_swap), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +static struct task_init task_init_swap_arp = { + .mode_str = "swap", + .sub_mode_str = "l3", + .init = init_task_swap, + .handle = handle_swap_bulk, + .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, + .size = sizeof(struct task_swap), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_swap(void) +{ + reg_task(&task_init_swap); + reg_task(&task_init_swap_arp); +} diff --git a/VNFs/DPPD-PROX/handle_swap.h b/VNFs/DPPD-PROX/handle_swap.h new file mode 100644 index 00000000..ef2fee04 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_swap.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HANDLE_SWAP_H_ +#define _HANDLE_SWAP_H_ + +struct task_base; +void task_swap_set_local_ip(struct task_base *tbase, uint32_t ip); + +#endif /* _HANDLE_SWAP_H_ */ diff --git a/VNFs/DPPD-PROX/handle_tsc.c b/VNFs/DPPD-PROX/handle_tsc.c new file mode 100644 index 00000000..e686aaa2 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_tsc.c @@ -0,0 +1,51 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mbuf.h> +#include <rte_cycles.h> + +#include "task_base.h" +#include "task_init.h" +#include "thread_generic.h" + +struct task_tsc { + struct task_base base; +}; + +static int handle_bulk_tsc(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_tsc *task = (struct task_tsc *)tbase; + const uint64_t rx_tsc = rte_rdtsc(); + + for (uint16_t j = 0; j < n_pkts; ++j) + mbufs[j]->udata64 = rx_tsc; + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, NULL); +} + +static struct task_init task_init = { + .mode_str = "tsc", + .init = NULL, + .handle = handle_bulk_tsc, + .flag_features = TASK_FEATURE_NEVER_DISCARDS|TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS|TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS|TASK_FEATURE_THROUGHPUT_OPT, + .size = sizeof(struct task_tsc), + .mbuf_size = 2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM, +}; + +__attribute__((constructor)) static void reg_task_nop(void) +{ + reg_task(&task_init); +} diff --git a/VNFs/DPPD-PROX/handle_untag.c b/VNFs/DPPD-PROX/handle_untag.c new file mode 100644 index 00000000..2fc8fe64 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_untag.c @@ -0,0 +1,144 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ip.h> + +#include "log.h" +#include "tx_pkt.h" +#include "task_base.h" +#include "task_init.h" +#include "mpls.h" +#include "defines.h" +#include "prefetch.h" +#include "qinq.h" +#include "prox_assert.h" +#include "etypes.h" + +struct task_untag { + struct task_base base; + uint16_t etype; +}; + +static void init_task_untag(struct task_base *tbase, __attribute__((unused)) struct task_args *targ) +{ + struct task_untag *task = (struct task_untag *)tbase; + task->etype = targ->etype; +} + +static inline uint8_t handle_untag(struct task_untag *task, struct rte_mbuf *mbuf); + +static int handle_untag_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_untag *task = (struct task_untag *)tbase; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + + prefetch_first(mbufs, n_pkts); + + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + out[j] = handle_untag(task, mbufs[j]); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + out[j] = handle_untag(task, mbufs[j]); + } +#endif + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static inline uint8_t untag_mpls(struct rte_mbuf *mbuf, struct ether_hdr *peth) +{ + struct ether_hdr *pneweth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, 4); + const struct mpls_hdr *mpls = (const struct mpls_hdr *)(peth + 1); + const struct ipv4_hdr *pip = (const struct ipv4_hdr *)(mpls + 1); + PROX_ASSERT(pneweth); + + if (mpls->bos == 0) { + // Double MPLS tag + pneweth = (struct ether_hdr *)rte_pktmbuf_adj(mbuf, 4); + PROX_ASSERT(pneweth); + } + + if ((pip->version_ihl >> 4) == 4) { + pneweth->ether_type = ETYPE_IPv4; + return 0; + } + else if ((pip->version_ihl >> 4) == 6) { + pneweth->ether_type = ETYPE_IPv6; + return 0; + } + + plog_warn("Failed Decoding MPLS Packet - neither IPv4 neither IPv6: version %u\n", pip->version_ihl); + return OUT_DISCARD; +} + +static uint8_t untag_qinq(struct rte_mbuf *mbuf, struct qinq_hdr *qinq) +{ + if ((qinq->cvlan.eth_proto != ETYPE_VLAN)) { + plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto); + return OUT_DISCARD; + } + + rte_pktmbuf_adj(mbuf, sizeof(struct qinq_hdr) - sizeof(struct ether_hdr)); + return 0; +} + +static inline uint8_t handle_untag(struct task_untag *task, struct rte_mbuf *mbuf) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + const uint16_t etype = peth->ether_type; + + if (etype != task->etype) { + plog_warn("Failed Removing %04x tag: ether_type = %#06x\n", task->etype, peth->ether_type); + return OUT_DISCARD; + } + + switch (etype) { + case ETYPE_MPLSU: + /* MPLS Decapsulation */ + return untag_mpls(mbuf, peth); + case ETYPE_LLDP: + return OUT_DISCARD; + case ETYPE_IPv6: + return 0; + case ETYPE_IPv4: + return 0; + case ETYPE_8021ad: + case ETYPE_VLAN: + return untag_qinq(mbuf, (struct qinq_hdr *)peth); + default: + plog_warn("Failed untagging header: ether_type = %#06x is not supported\n", peth->ether_type); + return OUT_DISCARD; + } +} + +static struct task_init task_init_untag = { + .mode_str = "untag", + .init = init_task_untag, + .handle = handle_untag_bulk, + .size = sizeof(struct task_untag) +}; + +__attribute__((constructor)) static void reg_task_untag(void) +{ + reg_task(&task_init_untag); +} diff --git a/VNFs/DPPD-PROX/hash_entry_types.h b/VNFs/DPPD-PROX/hash_entry_types.h new file mode 100644 index 00000000..e2cbcb3c --- /dev/null +++ b/VNFs/DPPD-PROX/hash_entry_types.h @@ -0,0 +1,71 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HASH_ENTRY_TYPES_H_ +#define _HASH_ENTRY_TYPES_H_ + +#include <rte_ether.h> + +struct ether_addr_port { + struct ether_addr mac; + uint8_t pad; + uint8_t out_idx; +}; + +struct next_hop { + uint32_t ip_dst; + uint32_t mpls; + union { + uint64_t mac_port_8bytes; + struct ether_addr_port mac_port; + }; +}; + +struct next_hop6 { + uint8_t ip_dst[16]; + uint32_t mpls; + union { + uint64_t mac_port_8bytes; + struct ether_addr_port mac_port; + }; +}; + +struct cpe_data { + uint16_t qinq_svlan; + uint16_t qinq_cvlan; + uint32_t user; + union { + uint64_t mac_port_8bytes; + struct ether_addr_port mac_port; + uint8_t mac_port_b[8]; + }; + uint64_t tsc; +}; + +struct cpe_key { + union { + uint32_t ip; + uint8_t ip_bytes[4]; + }; + uint32_t gre_id; +} __attribute__((__packed__)); + +struct qinq_gre_data { + uint32_t gre_id; + uint32_t user; +} __attribute__((__packed__)); + +#endif /* _HASH_ENTRY_TYPES_H_ */ diff --git a/VNFs/DPPD-PROX/hash_set.c b/VNFs/DPPD-PROX/hash_set.c new file mode 100644 index 00000000..5ea93e96 --- /dev/null +++ b/VNFs/DPPD-PROX/hash_set.c @@ -0,0 +1,105 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_hash_crc.h> +#include <string.h> + +#include "prox_malloc.h" +#include "prox_assert.h" +#include "hash_set.h" + +#define HASH_SET_ALLOC_CHUNCK 1024 +#define HASH_SET_ALLOC_CHUNCK_MEM (sizeof(struct hash_set_entry) * 1024) + +struct hash_set_entry { + uint32_t crc; + void *data; + size_t len; + struct hash_set_entry *next; +}; + +struct hash_set { + uint32_t n_buckets; + int socket_id; + struct hash_set_entry *alloc; + size_t alloc_count; + struct hash_set_entry *mem[0]; +}; + +static struct hash_set_entry *hash_set_alloc_entry(struct hash_set *hs) +{ + struct hash_set_entry *ret; + + if (hs->alloc_count == 0) { + size_t mem_size = HASH_SET_ALLOC_CHUNCK * + sizeof(struct hash_set_entry); + + hs->alloc = prox_zmalloc(mem_size, hs->socket_id); + hs->alloc_count = HASH_SET_ALLOC_CHUNCK; + } + + ret = hs->alloc; + hs->alloc++; + hs->alloc_count--; + return ret; +} + +struct hash_set *hash_set_create(uint32_t n_buckets, int socket_id) +{ + struct hash_set *ret; + size_t mem_size = sizeof(*ret) + sizeof(ret->mem[0]) * n_buckets; + + ret = prox_zmalloc(mem_size, socket_id); + ret->n_buckets = n_buckets; + ret->socket_id = socket_id; + + return ret; +} + +void *hash_set_find(struct hash_set *hs, void *data, size_t len) +{ + uint32_t crc = rte_hash_crc(data, len, 0); + + struct hash_set_entry *entry = hs->mem[crc % hs->n_buckets]; + + while (entry) { + if (entry->crc == crc && entry->len == len && + memcmp(entry->data, data, len) == 0) + return entry->data; + entry = entry->next; + } + return NULL; +} + +void hash_set_add(struct hash_set *hs, void *data, size_t len) +{ + uint32_t crc = rte_hash_crc(data, len, 0); + struct hash_set_entry *new = hash_set_alloc_entry(hs); + + new->data = data; + new->len = len; + new->crc = crc; + + if (hs->mem[crc % hs->n_buckets]) { + struct hash_set_entry *entry = hs->mem[crc % hs->n_buckets]; + while (entry->next) + entry = entry->next; + entry->next = new; + } + else { + hs->mem[crc % hs->n_buckets] = new; + } +} diff --git a/VNFs/DPPD-PROX/hash_set.h b/VNFs/DPPD-PROX/hash_set.h new file mode 100644 index 00000000..72345215 --- /dev/null +++ b/VNFs/DPPD-PROX/hash_set.h @@ -0,0 +1,26 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HASH_SET_H_ +#define _HASH_SET_H_ + +struct hash_set; + +struct hash_set *hash_set_create(uint32_t n_buckets, int socket_id); +void *hash_set_find(struct hash_set *hs, void *data, size_t len); +void hash_set_add(struct hash_set *hs, void *data, size_t len); + +#endif /* _HASH_SET_H_ */ diff --git a/VNFs/DPPD-PROX/hash_utils.c b/VNFs/DPPD-PROX/hash_utils.c new file mode 100644 index 00000000..4ebab94e --- /dev/null +++ b/VNFs/DPPD-PROX/hash_utils.c @@ -0,0 +1,184 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <rte_hash_crc.h> +#include <rte_table_hash.h> +#include <rte_version.h> + +#include "hash_utils.h" + +/* These opaque structure definitions were copied from DPDK lib/librte_table/rte_table_hash_key8.c */ + +struct rte_bucket_4_8 { + /* Cache line 0 */ + uint64_t signature; + uint64_t lru_list; + struct rte_bucket_4_8 *next; + uint64_t next_valid; + + uint64_t key[4]; + + /* Cache line 1 */ + uint8_t data[0]; +}; + +struct rte_table_hash_key8 { +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + struct rte_table_stats stats; +#endif + /* Input parameters */ + uint32_t n_buckets; + uint32_t n_entries_per_bucket; + uint32_t key_size; + uint32_t entry_size; + uint32_t bucket_size; + uint32_t signature_offset; + uint32_t key_offset; +#if RTE_VERSION >= RTE_VERSION_NUM(2,2,0,0) + uint64_t key_mask; +#endif + rte_table_hash_op_hash f_hash; + uint64_t seed; + + /* Extendible buckets */ + uint32_t n_buckets_ext; + uint32_t stack_pos; + uint32_t *stack; + + /* Lookup table */ + uint8_t memory[0] __rte_cache_aligned; +}; + +/* These opaque structure definitions were copied from DPDK lib/librte_table/rte_table_hash_ext.c */ + +struct bucket { + union { + uintptr_t next; + uint64_t lru_list; + }; + uint16_t sig[4]; + uint32_t key_pos[4]; +}; + +#define BUCKET_NEXT(bucket) \ + ((void *) ((bucket)->next & (~1LU))) + +struct grinder { + struct bucket *bkt; + uint64_t sig; + uint64_t match; + uint32_t key_index; +}; + +struct rte_table_hash_ext { +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + struct rte_table_stats stats; +#endif + /* Input parameters */ + uint32_t key_size; + uint32_t entry_size; + uint32_t n_keys; + uint32_t n_buckets; + uint32_t n_buckets_ext; + rte_table_hash_op_hash f_hash; + uint64_t seed; + uint32_t signature_offset; + uint32_t key_offset; + + /* Internal */ + uint64_t bucket_mask; + uint32_t key_size_shl; + uint32_t data_size_shl; + uint32_t key_stack_tos; + uint32_t bkt_ext_stack_tos; + + /* Grinder */ + struct grinder grinders[64]; + + /* Tables */ + struct bucket *buckets; + struct bucket *buckets_ext; + uint8_t *key_mem; + uint8_t *data_mem; + uint32_t *key_stack; + uint32_t *bkt_ext_stack; + + /* Table memory */ + uint8_t memory[0] __rte_cache_aligned; +}; + +uint64_t get_bucket(void* table, uint32_t bucket_idx, void** key, void** entries) +{ + struct rte_table_hash_ext *t = (struct rte_table_hash_ext *) table; + struct bucket *bkt0, *bkt, *bkt_prev; + uint64_t sig; + uint32_t bkt_index, i; + uint8_t n = 0; + bkt_index = bucket_idx & t->bucket_mask; + bkt0 = &t->buckets[bkt_index]; + sig = (bucket_idx >> 16) | 1LLU; + + /* Key is present in the bucket */ + for (bkt = bkt0; bkt != NULL; bkt = BUCKET_NEXT(bkt)) { + for (i = 0; i < 4; i++) { + uint64_t bkt_sig = (uint64_t) bkt->sig[i]; + uint32_t bkt_key_index = bkt->key_pos[i]; + uint8_t *bkt_key = + &t->key_mem[bkt_key_index << t->key_size_shl]; + + if (sig == bkt_sig) { + key[n] = bkt_key; + entries[n++] = &t->data_mem[bkt_key_index << t->data_size_shl]; + /* Assume no more than 4 entries in total (including extended state) */ + if (n == 4) + return t->n_buckets; + } + } + } + return t->n_buckets; +} + +uint64_t get_bucket_key8(void* table, uint32_t bucket_idx, void** key, void** entries) +{ + struct rte_bucket_4_8 *bucket, *bucket0; + struct rte_table_hash_key8* f = table; + uint8_t n = 0; + + bucket0 = (struct rte_bucket_4_8 *) &f->memory[bucket_idx * f->bucket_size]; + for (bucket = bucket0; bucket != NULL; bucket = bucket->next) { + uint64_t mask; + + for (uint8_t i = 0, mask = 1LLU; i < 4; i++, mask <<= 1) { + uint64_t bucket_signature = bucket->signature; + + if (bucket_signature & mask) { + key[n] = &bucket->key[i]; + entries[n++] = &bucket->data[i *f->entry_size]; + /* Assume no more than 4 entries + in total (including extended state) */ + if (n == 4) + return f->n_buckets; + } + } + } + return f->n_buckets; +} + +uint64_t hash_crc32(void* key, uint32_t key_size, uint64_t seed) +{ + return rte_hash_crc(key, key_size, seed); +} diff --git a/VNFs/DPPD-PROX/hash_utils.h b/VNFs/DPPD-PROX/hash_utils.h new file mode 100644 index 00000000..a2536ffb --- /dev/null +++ b/VNFs/DPPD-PROX/hash_utils.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HASH_UTILS_H_ +#define _HASH_UTILS_H_ + +#include <rte_common.h> +#include <rte_version.h> + +struct rte_table_hash; + +/* Take DPDK 2.2.0 ABI change into account: offset 0 now means first byte of mbuf struct + * see http://www.dpdk.org/browse/dpdk/commit/?id=ba92d511ddacf863fafaaa14c0577f30ee57d092 + */ +#if RTE_VERSION >= RTE_VERSION_NUM(2,2,0,0) +#define HASH_METADATA_OFFSET(offset) (sizeof(struct rte_mbuf) + (offset)) +#else +#define HASH_METADATA_OFFSET(offset) (offset) +#endif + +/* Wrap crc32 hash function to match that required for rte_table */ +uint64_t hash_crc32(void* key, uint32_t key_size, uint64_t seed); + +void print_hash_table_size(const struct rte_table_hash *h); +void print_hash_table(const struct rte_table_hash *h); + +uint64_t get_bucket_key8(void* table, uint32_t bucket_idx, void** key, void** entries); +uint64_t get_bucket(void* table, uint32_t bucket_idx, void** key, void** entries); +#endif /* _HASH_UTILS_H_ */ diff --git a/VNFs/DPPD-PROX/heap.c b/VNFs/DPPD-PROX/heap.c new file mode 100644 index 00000000..69b0736e --- /dev/null +++ b/VNFs/DPPD-PROX/heap.c @@ -0,0 +1,515 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> +#include <stddef.h> +#include <rte_version.h> +#include <rte_prefetch.h> +#include <rte_memory.h> + +#include "prox_malloc.h" +#include "prox_assert.h" +#include "heap.h" +#include "log.h" + +#include <string.h> +#include <stddef.h> +#include <stdlib.h> + +struct heap_elem { + uint64_t priority; + struct heap_ref *ref; + struct heap_elem *prev; + struct heap_elem *next; + struct heap_elem *child; +}; + +struct strl { + char *str; + size_t len; +}; + +int heap_top_is_lower(struct heap *h, uint64_t prio) +{ + return !heap_is_empty(h) && h->top->priority < prio; +} + +static int heap_elem_check(struct heap_elem *e, int is_top) +{ + if (!e) + return 1; + if (e != e->prev && + e != e->next && + e != e->child) + return 1; + else + return 0; + + if (is_top && e->prev != NULL) + return 0; + if (!is_top && e->prev == NULL) + return 0; + + if (e->next) { + if (e->next->prev != e) + return 0; + + if (heap_elem_check(e->next, 0)) + return 1; + else + return 0; + } + + if (e->child) { + if (e->child->prev != e) + return 0; + + if (heap_elem_check(e->child, 0)) + return 1; + else + return 0; + } + + return 1; +} + +static int heap_elem_in_heap_elem(struct heap_elem *in, struct heap_elem *find) +{ + if (in == find) + return 1; + + if (in->next) { + if (heap_elem_in_heap_elem(in->next, find)) + return 1; + } + if (in->child) { + if (heap_elem_in_heap_elem(in->child, find)) + return 1; + } + + return 0; +} + +static int heap_elem_in_heap(struct heap *h, struct heap_elem *e) +{ + if (h->top == NULL) + return 0; + + return heap_elem_in_heap_elem(h->top, e); +} + +static int heap_elem_is_avail(struct heap *h, struct heap_elem *e) +{ + for (uint32_t i = 0; i < h->n_avail; ++i) { + if (h->avail[i] == e) + return 1; + } + return 0; +} + +static uint32_t heap_elem_calc_size(struct heap_elem *e) +{ + int ret = 0; + + if (e) + ret++; + else + return ret; + + if (e->next) + ret += heap_elem_calc_size(e->next); + if (e->child) + ret += heap_elem_calc_size(e->child); + return ret; +} + +static uint32_t heap_calc_size(struct heap *h) +{ + return heap_elem_calc_size(h->top); +} + +static void cat_indent(struct strl *s, int indent) +{ + size_t r; + + if (s->len < 50) + return ; + + for (int i = 0; i < indent; ++i) { + r = snprintf(s->str, s->len, " "); + s->str += r; + s->len -= r; + } +} + +static void cat_priority(struct strl *s, uint64_t priority) +{ + size_t r; + + if (s->len < 50) + return ; + + r = snprintf(s->str, s->len, "%"PRIu64"\n", priority); + s->str += r; + s->len -= r; +} + +static void heap_print2(struct heap_elem *e, int indent, struct strl *s) +{ + size_t r; + + cat_indent(s, indent); + cat_priority(s, e->priority); + + struct heap_elem *child = e->child; + + while (child) { + heap_print2(child, indent + 1, s); + child = child->next; + } +} + +static void heap_print3(struct heap_elem *e, char *result, size_t buf_len) +{ + struct strl s; + + s.str = result; + s.len = buf_len; + + heap_print2(e, 0, &s); +} + +void heap_print(struct heap *h, char *result, size_t buf_len) +{ + if (h->n_elems == 0) { + *result = 0; + return ; + } + + heap_print3(h->top, result, buf_len); +} + +struct heap *heap_create(uint32_t max_elems, int socket_id) +{ + struct heap *ret; + size_t mem_size = 0; + size_t elem_mem = 0; + struct heap_elem *e; + + /* max_elems + 1 since index start at 1. Store total number of + elements in the first entry (which is unused otherwise). */ + mem_size += sizeof(struct heap); + mem_size += sizeof(((struct heap *)0)->top) * max_elems; + mem_size = RTE_CACHE_LINE_ROUNDUP(mem_size); + elem_mem = mem_size; + mem_size += sizeof(*((struct heap *)0)->top) * max_elems; + ret = prox_zmalloc(mem_size, socket_id); + if (!ret) + return NULL; + + e = (struct heap_elem *)(((uint8_t *)ret) + elem_mem); + PROX_ASSERT((void *)&e[max_elems] <= (void *)ret + mem_size); + + for (uint32_t i = 0; i < max_elems; ++i) { + PROX_ASSERT(e->priority == 0); + PROX_ASSERT(e->ref == 0); + PROX_ASSERT(e->prev == 0); + PROX_ASSERT(e->next == 0); + PROX_ASSERT(e->child == 0); + + ret->avail[ret->n_avail++] = e++; + } + + PROX_ASSERT(ret->n_elems + ret->n_avail == max_elems); + return ret; +} + +static struct heap_elem *heap_get(struct heap *h) +{ + PROX_ASSERT(h->n_avail); + + return h->avail[--h->n_avail]; +} + +static void heap_put(struct heap *h, struct heap_elem *e) +{ + h->avail[h->n_avail++] = e; +} + +void heap_add(struct heap *h, struct heap_ref *ref, uint64_t priority) +{ + PROX_ASSERT(h); + PROX_ASSERT(ref); + PROX_ASSERT(ref->elem == NULL); + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); + + if (h->n_elems == 0) { + h->n_elems++; + h->top = heap_get(h); + + h->top->priority = priority; + h->top->ref = ref; + ref->elem = h->top; + h->top->prev = NULL; + h->top->next = NULL; + h->top->child = NULL; + + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); + return ; + } + + h->n_elems++; + /* New element becomes new top */ + if (h->top->priority > priority) { + struct heap_elem *n = heap_get(h); + + n->priority = priority; + n->ref = ref; + ref->elem = n; + n->prev = NULL; + n->next = NULL; + n->child = h->top; + + h->top->prev = n; + h->top = n; + } + /* New element is added as first sibling */ + else { + struct heap_elem *n = heap_get(h); + n->priority = priority; + n->ref = ref; + ref->elem = n; + n->prev = h->top; + n->next = h->top->child; + if (h->top->child) + h->top->child->prev = n; + n->child = NULL; + h->top->child = n; + } + + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); +} + +static void heap_merge_tops_left(struct heap_elem *left, struct heap_elem *right) +{ + PROX_ASSERT(left->priority <= right->priority); + PROX_ASSERT(left != right); + + /* right moves down and becomes first child of left. */ + left->next = right->next; + if (right->next) + right->next->prev = left; + + right->next = left->child; + if (left->child) + left->child->prev = right; + + /* right->prev is now referring to parent since right is the + new first child. */ + left->child = right; +} + +static void heap_merge_tops_right(struct heap_elem *left, struct heap_elem *right) +{ + PROX_ASSERT(left->priority >= right->priority); + PROX_ASSERT(left != right); + + /* Left goes down one layer */ + right->prev = left->prev; + if (left->prev) + left->prev->next = right; + + left->next = right->child; + if (right->child) + right->child->prev = left; + + left->prev = right; + right->child = left; +} + +static struct heap_elem *heap_merge_children(struct heap_elem *e) +{ + struct heap_elem *next = e->next; + struct heap_elem *tmp; + struct heap_elem *prev; + struct heap_elem *first; + + PROX_ASSERT(e); + int cnt = 0; + /* TODO: is this really needed? */ + if (!next) + return e; + + if (e->priority < next->priority) + first = e; + else + first = next; + + /* Forward pass */ + do { + cnt++; + tmp = next->next; + rte_prefetch0(tmp); + if (e->priority < next->priority) { + heap_merge_tops_left(e, next); + prev = e; + PROX_ASSERT(e->child == next); + } + else { + heap_merge_tops_right(e, next); + PROX_ASSERT(next->child == e); + prev = next; + } + + if (tmp) { + tmp->prev = prev; + e = tmp; + /* Next could be empty, (uneven # children) */ + if (!tmp->next) + break; + next = tmp->next; + } + else { + /* Even number of nodes, after breaking set e + to the last merged pair top */ + if (e->priority >= next->priority) + e = next; + break; + } + } while (1); + /* Backward pass, merge everything with the right until the + first child */ + while (first != e) { + prev = e->prev; + + if (e->priority < prev->priority) { + heap_merge_tops_right(prev, e); + if (prev == first) { + first = e; + break; + } + } + else { + heap_merge_tops_left(prev, e); + e = prev; + } + } + return first; +} + +static int heap_elem_first_sibling(const struct heap_elem *e) +{ + return e->prev->child == e; +} + +void heap_del(struct heap *h, struct heap_ref *d) +{ + struct heap_elem *del = d->elem; + + PROX_ASSERT(del); + PROX_ASSERT(heap_elem_in_heap(h, del)); + PROX_ASSERT(!heap_elem_is_avail(h, del)); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->top->next == NULL); + PROX_ASSERT(h->top->prev == NULL); + + d->elem = NULL; + /* Del is at the top */ + if (del->prev == NULL) { + PROX_ASSERT(del == h->top); + if (del->child) { + del->child->prev = NULL; + h->top = heap_merge_children(del->child); + PROX_ASSERT(h->top); + } + else { + h->top = NULL; + } + + h->n_elems--; + heap_put(h, del); + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->n_elems == 0 || h->top != NULL); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); + return ; + } + PROX_ASSERT(del != h->top); + + /* Del is somewhere in a lower layer. If it the first child, + need to fix the parent differently. */ + if (heap_elem_first_sibling(del)) { + del->prev->child = del->next; + if (del->next) + del->next->prev = del->prev; + } + else { + del->prev->next = del->next; + if (del->next) + del->next->prev = del->prev; + } + + struct heap_elem *top2 = del->child; + + /* If the node to be deleted has children, there is more work: + merge the children into a single heap and merge with + top. If there are no children, then the disconnection above + is enough. */ + if (top2) { + top2->prev = NULL; + top2 = heap_merge_children(top2); + + /* Merge top2 with h->top */ + if (h->top->priority < top2->priority) { + top2->next = h->top->child; + top2->prev = h->top; + if (h->top->child) + h->top->child->prev = top2; + + h->top->child = top2; + } + else { + h->top->next = top2->child; + h->top->prev = top2; + if (top2->child) + top2->child->prev = h->top; + + top2->child = h->top; + h->top = top2; + } + + } + h->n_elems--; + heap_put(h, del); + + PROX_ASSERT(heap_elem_check(h->top, 1)); + PROX_ASSERT(h->n_elems == heap_calc_size(h)); +} + +struct heap_ref *heap_pop(struct heap *h) +{ + if (h->n_elems == 0) + return NULL; + + struct heap_ref *ret = h->top->ref; + + heap_del(h, h->top->ref); + return ret; +} diff --git a/VNFs/DPPD-PROX/heap.h b/VNFs/DPPD-PROX/heap.h new file mode 100644 index 00000000..08e5f1a5 --- /dev/null +++ b/VNFs/DPPD-PROX/heap.h @@ -0,0 +1,53 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _HEAP_H_ +#define _HEAP_H_ + +#include <inttypes.h> +#include <stdlib.h> + +struct heap_ref { + struct heap_elem *elem; /* timer management */ +}; + +struct heap { + uint64_t n_elems; + struct heap_elem *top; + uint64_t n_avail; + struct heap_elem *avail[0]; +}; + +static uint64_t heap_n_elems(const struct heap *h) +{ + return h->n_elems; +} + +static int heap_is_empty(const struct heap *h) +{ + return !h->n_elems; +} + +int heap_top_is_lower(struct heap *h, uint64_t prio); + +void heap_print(struct heap *h, char *result, size_t buf_len); + +struct heap *heap_create(uint32_t max_elems, int socket_id); +void heap_add(struct heap *h, struct heap_ref *ref, uint64_t priority); +void heap_del(struct heap *h, struct heap_ref *del); +struct heap_ref *heap_pop(struct heap *h); + +#endif /* _HEAP_H_ */ diff --git a/VNFs/DPPD-PROX/helper-scripts/demo-scripts/prox.py b/VNFs/DPPD-PROX/helper-scripts/demo-scripts/prox.py new file mode 100644 index 00000000..f9250d21 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/demo-scripts/prox.py @@ -0,0 +1,53 @@ +#!/bin/env python2.7 + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import socket + +class prox: + def __init__(self, ip): + self._ip = ip; + self._dat = "" + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((self._ip, 8474)) + except: + raise Exception("Failed to connect to PROX on " + self._ip + ":8474") + self._sock = sock; + + def send(self, msg): + self._sock.sendall(msg + "\n"); + return self + def recv(self): + ret_str = ""; + done = 0; + while done == 0: + if (len(self._dat) == 0): + self._dat = self._sock.recv(256); + + while(len(self._dat)): + if (self._dat[0] == '\n'): + done = 1 + self._dat = self._dat[1:] + break; + else: + ret_str += self._dat[0]; + self._dat = self._dat[1:] + return ret_str; + + def wait_cmd_finished(self): + self.send("stats hz").recv(); diff --git a/VNFs/DPPD-PROX/helper-scripts/demo-scripts/tx_rate.py b/VNFs/DPPD-PROX/helper-scripts/demo-scripts/tx_rate.py new file mode 100644 index 00000000..112b583a --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/demo-scripts/tx_rate.py @@ -0,0 +1,74 @@ +#!/bin/env python2.7 + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from prox import * +from decimal import * +from time import * + +class data_point: + value = 0; + tsc = 0; + def __init__(self, value, tsc): + self.value = value; + self.tsc = tsc; + +def measure_tx(prox_instance, port_id): + port_tx_pkt = "port(" + str(port_id) + ").tx.packets" + port_tsc = "port(" + str(port_id) + ").tsc"; + cmd = "stats " + port_tx_pkt + "," + port_tsc; + reply = prox_instance.send(cmd).recv().split(","); + + return data_point(int(reply[0]), int(reply[1])); + +def get_rate(first, second, hz): + tsc_diff = second.tsc - first.tsc; + value_diff = second.value - first.value; + + return int(Decimal(value_diff * hz) / tsc_diff) + +# make sure that prox has been started with the -t parameter +prox_instance = prox("127.0.0.1") +print "Connected to prox" + +hz = int(prox_instance.send("stats hz").recv()); + +print "System is running at " + str(hz) + " Hz" + +print "Showing TX pps on port 0" + +update_interval = 0.1 + +print "Requesting new data every " + str(update_interval) + "s" + +measure = measure_tx(prox_instance, 0); +while (True): + sleep(update_interval) + measure2 = measure_tx(prox_instance, 0); + + # since PROX takes measurements at a configured rate (through + # update interval command or throw -r command line parameter), it + # might be possible that two consecutive measurements report the + # same. To get updates at a frequency higher than 1 Hz, + # reconfigure prox as mentioned above. + + if (measure.tsc == measure2.tsc): + continue; + + print get_rate(measure, measure2, hz); + + measure = measure2; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/README b/VNFs/DPPD-PROX/helper-scripts/dpi/README new file mode 100644 index 00000000..f1100757 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/README @@ -0,0 +1,41 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +The scripts in this directory characterize flow a DPI-enabled VNF. The +characeterization is split up into two steps. The first step (dpi1.py) +searches for the traffic profile parameter boundaries. The second step +(dpi2.py) takes as input the output of the first step and searches for +the maximum sustainable throughput of a DPI-enabled VNF. + +To run the first script, use: + + python2.7 ./dpi1.py -t TEST_SYSTEM_DESCRIPTIONS -o OUTPUT1 + +TEST_SYSTEM_DESCRIPTIONS is a comma-separated list of systems where +the syntax of defining each system is shown below: + + user@ip:proxDir:cfgDir + +To run the second script, use: + + python2.7 ./dpi2.py -t TEST_SYSTEM_DESCRIPTIONS \ + -s SYSTEM_UNDER_TEST_DESCRIPTIONS \ + -o OUTPUT2 -d \ + -i OUTPUT1 + +Finally, the results can be processed using the following command: + + python2.7 ./maketable.py -i OUTPUT1 -j OUTPUT2 -o FINAL_TABLE diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/config.py b/VNFs/DPPD-PROX/helper-scripts/dpi/config.py new file mode 100644 index 00000000..ee3f04c6 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/config.py @@ -0,0 +1,178 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import getopt +import sys +from systemconfig import * + +class Config: + _debug = False; + _test_systems = []; + _output_file_name = None; + _input_file_name = None + _input_file_name2 = None + _max_port_rate = 0.85 + _sut = None + _accuracy = 2; + _threshold = 0.95 + _once = None + _skipTime = 10 + _testLength = 120 + _dpiCoreList = range(1, 5) + _checkConditions = False; + _interCheckDuration = float(1) + + def getInputFileName(self): + return self._input_file_name + + def getInputFileName2(self): + return self._input_file_name2 + + def toString(self): + ret = "" + ret += "Test systems: \n" + for ts in self._test_systems: + ret += ts.toString(); + + if (self._sut is not None): + ret += "SUT: \n" + ret += self._sut.toString(); + + ret += "Output file name: " + str(self._output_file_name) + "\n" + ret += "Max port rate: " + str(self._max_port_rate) + "\n" + ret += "Accuracy: " + str(self._accuracy) + " digits after point" + return ret + + def getErrorTestOne(self): + if (len(self._test_systems) == 0): + return "Missing test systems"; + if (self._output_file_name is None): + return "No output file or input file defined"; + return None + + def getErrorTestTwo(self): + if (self._input_file_name is None): + return "Input file is missing" + if (self._input_file_name == self._output_file_name): + return "Input file and output file are the same" + return self.getErrorTestOne(); + + def getErrorMakeTable(self): + if (self._input_file_name is None): + return "Missing input file" + if (self._input_file_name2 is None): + return "Missing file with performance resuilts" + if (self._output_file_name is None): + return "No output file or input file defined"; + if (self._input_file_name2 == self._input_file_name): + return "Input file used multiple times" + if (self._input_file_name == self._output_file_name): + return "output file is the same as the input file" + if (self._input_file_name2 == self._output_file_name): + return "output file is the same as the input file 2" + + return None + + def usageAndExit(self, argv0): + print "Usage: " + str(argv0) + print "-t Add a test system, syntax: " + SystemConfig.expectedSyntax() + print "-s Add SUT, syntax: " + SystemConfig.expectedSyntax() + print "-o Ouput file name" + print "-a Accuracy, number of digits after point" + print "-i Input file" + print "-j File with performance results" + print "-m Maximum per port rate, by default 0.85 (85%)" + print "-d Enable debugging" + print "-w Fraction of connections to reach, by default is 0.95 (95%)" + print "-h Show help" + print "-q Run a single test iteration, syntax of argument " + print "-b Skip time, by default 10 sec" + print "-l Test length, by default 120 sec" + print "-n Maximum number of DPI cores to test" + print "-k Period between checking conditions, 1 second by default" + print "-c Check conditions during 10 second period after convergence" + print " is msr,conn,ss (i.e. -q 4000,100000,38.91)" + exit(-1); + + def parse(self, programName, args): + try: + opts, args = getopt.getopt(args, "t:s:o:a:i:q:m:dhw:j:b:l:n:k:c") + except getopt.GetoptError as err: + print str(err) + return; + for option, arg in opts: + if(option == "-t"): + for ts in arg.split(","): + syntaxErr = SystemConfig.checkSyntax(ts) + if (syntaxErr != ""): + print syntaxErr + exit(-1); + self._test_systems.append(SystemConfig(ts)); + elif(option == "-s"): + syntaxErr = SystemConfig.checkSyntax(ts) + if (syntaxErr != ""): + print syntaxErr + exit(-1); + self._sut = SystemConfig(arg); + elif(option == "-w"): + self._threshold = float(arg) + elif(option == "-o"): + self._output_file_name = arg; + elif(option == '-a'): + self._accuracy = int(arg); + elif(option == "-i"): + self._input_file_name = arg; + elif(option == "-j"): + self._input_file_name2 = arg; + elif(option == "-q"): + self._once = arg.split(",") + elif(option == "-c"): + self._checkConditions = True; + elif(option == "-m"): + self._max_port_rate = float(arg); + elif(option == "-k"): + self._interCheckDuration = float(arg); + elif(option == "-d"): + self._debug = True + elif(option == '-h'): + self.usageAndExit(programName) + elif(option == '-b'): + self._skipTime = int(arg) + elif(option == '-l'): + self._testLength = int(arg) + elif(option == '-n'): + self._dpiCoreList = self.strToList(arg) + else: + self.usageAndExit(programName); + + def strToList(self, arg): + elements = []; + tokens = arg.split(","); + + for a in tokens: + if (a.count('-') == 0): + elements.append(int(a)) + elif (a.count('-') == 1): + beg = int(a.split('-')[0]); + end = int(a.split('-')[1]); + if (beg > end): + raise Exception("Invalid list input format") + elements += range(beg, end + 1); + else: + raise Exception("Invalid list input format") + return elements; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/csvreader.py b/VNFs/DPPD-PROX/helper-scripts/dpi/csvreader.py new file mode 100644 index 00000000..b0b650dc --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/csvreader.py @@ -0,0 +1,78 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from decimal import * + +class CsvReaderError: + def __init__(self, msg): + self._msg = msg; + + def __str__(self): + return self._msg; + +class CsvReader: + def __init__(self, fieldTypes = None): + self._file_name = None; + self._fieldTypes = fieldTypes; + + def open(self, file_name): + self._file = open(file_name, 'r'); + self._file_name = file_name; + + def read(self): + line = "#" + while (len(line) != 0 and line[0] == "#"): + line = self._file.readline(); + + if (len(line) != 0): + return self._lineToEntry(line) + else: + return None; + + def _lineToEntry(self, line): + split = line.strip().split(','); + if (self._fieldTypes is None): + return split; + have = len(split) + expected = len(self._fieldTypes) + if (have != expected): + raise CsvReaderError("Invalid number of fields %d != %d" % (have, expected)) + + entry = {}; + for i in range(len(self._fieldTypes)): + curFieldType = self._fieldTypes[i][1] + curFieldName = self._fieldTypes[i][0]; + if (curFieldType == "int"): + entry[curFieldName] = int(split[i]) + elif (curFieldType == "Decimal"): + entry[curFieldName] = Decimal(split[i]) + else: + raise CsvReaderError("Invalid field type %s" % curFieldType); + return entry; + + def readAll(self): + ret = [] + line = self.read(); + while (line != None): + ret.append(line); + line = self.read(); + return ret; + + def close(self): + self._file.close(); + self._file = None; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/csvwriter.py b/VNFs/DPPD-PROX/helper-scripts/dpi/csvwriter.py new file mode 100644 index 00000000..a5f055e8 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/csvwriter.py @@ -0,0 +1,35 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +class CsvWriter: + def __init__(self): + self._file_name = None; + + def open(self, file_name): + self._file = open(file_name, 'w'); + self._file_name = file_name; + + def write(self, elements): + elements_str = map(lambda x: str(x), elements); + line = ",".join(elements_str); + self._file.write(line + "\n"); + self._file.flush(); + + def close(self): + self._file.close(); + self._file = None; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/dpi1.py b/VNFs/DPPD-PROX/helper-scripts/dpi/dpi1.py new file mode 100644 index 00000000..ec3e4a03 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/dpi1.py @@ -0,0 +1,243 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from testerset import * +from time import sleep +from time import time +from decimal import * +import copy +from os import system +import socket +from itertools import chain +from math import * +from csvwriter import * +from config import * +from progress import * +from proxmaxssprobe import * + +def runTest(minSetupRate, testParam): + print "Running test with following parameters:" + print testParam.toString(); + + testers = testerSet(config._test_systems, config._max_port_rate, testParam); + + thresh = testParam.getConnections(); + p = Progress(thresh, ["connections", "setup rate", "reTX"], False); + loop_count = 0; + converged = False; + + testers.startForkJoin(); + testers.wait_links_up(); + testers.start_cores(); + + print "Running until convergence (%s connections)" % str(thresh) + while (not converged): + sleep(config._interCheckDuration) + testers.update_stats(); + tot = testers.get_total_connections(); + tot_retx = testers.get_total_retx(); + rates = testers.get_rates(); + curSetupRate = testers.get_setup_rate(); + ierrors = testers.getIerrors(); + + converged = tot >= thresh; + if (not converged): + if (loop_count > 0 and curSetupRate < minSetupRate): + reason = str(curSetupRate) + " < " + str(minSetupRate); + print "Current setup rate is lower than min setup rate: " + reason + testers.killProx(); + return False, []; + if (not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + testers.killProx(); + return False, []; + + if (config._debug): + p.setProgress(tot, [tot, curSetupRate, tot_retx]); + print p.toString(); + loop_count += 1; + print "converged" + + skipTime = config._skipTime + print "Connection threshold reached, waiting for " + str(skipTime) + "s, conditions checked = " + str(config._checkConditions) + while (skipTime > 0): + skipTime -= config._interCheckDuration + sleep(config._interCheckDuration) + testers.update_stats(); + if (config._checkConditions and not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + testers.killProx(); + return False, []; + + testers.tx_rate_meassurement(); + + testLength = config._testLength + print "Waiting final " + str(testLength) + "s" + while (testLength > 0): + testLength -= config._interCheckDuration + sleep(config._interCheckDuration) + testers.update_stats(); + if (not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + testers.killProx(); + return False, []; + + rates = testers.tx_rate_meassurement(); + + testers.killProx(); + return True, rates; + +def find_ss(tot_conn, maxSetupRate, ss_max): + iterationCount = 0; + valid_ss = [] + speed_ss = []; + + # The setup rate must be in [0.2% of total connections, maxSetupRate] + # Also, it must not be hihger than 50% of the total connections + min_setup_rate = tot_conn / 500; + + if (min_setup_rate > maxSetupRate): + print "min setup rate > max setup rate: " + str(min_setup_rate) + " > " + str(maxSetupRate); + return valid_ss, speed_ss; + if (maxSetupRate > tot_conn / 2): + print "maximum setup rate (" + str(maxSetupRate) + ") is more than 50% of " + str(tot_conn) + return valid_ss, speed_ss; + + accuracy = 10**config._accuracy + ss_lo = 1 + ss_hi = int(round(ss_max * accuracy,0)) + + iterationOverride = [ss_hi, ss_lo]; + # Binary search for highest speed scaling + while (ss_lo <= ss_hi): + if (iterationCount < len(iterationOverride)): + ss = iterationOverride[iterationCount] + else: + ss = (ss_lo + ss_hi)/2; + + testParam = TestParameters(maxSetupRate, tot_conn, float(ss)/accuracy); + + success, rates = runTest(min_setup_rate, testParam); + print "success = " + str(success) + ", rates = " + str(rates) + if (success == True): + valid_ss.append(float(ss)/accuracy); + speed_ss.append(sum(rates)/len(rates)) + ss_lo = ss + 1 + else: + ss_hi = ss - 1; + iterationCount += 1 + return valid_ss, speed_ss; + +def get_highest_ss_and_speed(valid_ss, speed_ss): + highest_ss = None; + highest_speed = None; + + for i in range(len(valid_ss)): + if(highest_ss == None or highest_ss < valid_ss[i]): + highest_ss = valid_ss[i]; + highest_speed = speed_ss[i]; + return highest_ss, highest_speed; + +def get_max_ss(): + ts = config._test_systems[0]; + test_system = ProxMaxSSProbe(ts); + max_ss = test_system.getMaxSS(); + + return floor((max_ss * (10**config._accuracy)))/(10**config._accuracy) + +config = Config(); +config.parse(sys.argv[0], sys.argv[1:]) + +err = config.getErrorTestOne(); +if (err is not None): + print "Invalid configuration: " + err; + exit(-1); +else: + print config.toString() + +if (config._once is not None): + maxSetupRate = int(config._once[0]) + minSetupRate = maxSetupRate/500 + connections = int(config._once[1]) + speedScaling = float(config._once[2]) + + testParam = TestParameters(maxSetupRate, connections, speedScaling) + success, rates = runTest(minSetupRate, testParam) + print "success = " + str(success) + ", port rates = " + str(rates) + exit(0); + +msr_list = [] +msr_list += range(4000, 20000, 2000) +msr_list += range(20000, 100000, 20000) +msr_list += range(100000, 300000, 50000) +msr_list += range(300000, 800001, 100000); + +conn_list = [1*10**5, 2*10**5, 4*10**5, 8*10**5, 1*10**6, 2*10**6] + +summary_file = CsvWriter() +summary_file.open(config._output_file_name) + +tot_it = 0; +for tot_conn in conn_list: + for msr in msr_list: + if (msr >= tot_conn/2): + break; + tot_it += 1 + +cnt = -1; +print "Search will include " + str(tot_it) + " parameter combinations" +print "Will search for highest link utilization" + +# If the lowest msr was a for n connections, then the lowest msr +# for n + 1 connections can't be lower than a. +low_sr = msr_list[0]; + +max_ss = get_max_ss() + +high_ss = Decimal(max_ss) + +globalProgress = Progress(tot_it) +globalProgress.setProgress(0); +for tot_conn in conn_list: + had_success = False; + all_ss = [] + for msr in msr_list: + globalProgress.incrProgress(); + + if (msr < low_sr): + print "skipping " + str(msr) + " since it is lower than " + str(low_sr) + continue; + + print globalProgress.toString(); + + valid_ss, speed_ss = find_ss(tot_conn, msr, high_ss) + print "valid ss = " + str(valid_ss) + print "valid speeds = " + str(speed_ss) + + if (len(valid_ss) > 0): + highest_ss, highest_speed = get_highest_ss_and_speed(valid_ss, speed_ss); + summary_file.write([msr, tot_conn, highest_ss, highest_speed]); + + if (not had_success): + low_sr = msr; + + had_success = True; + all_ss = all_ss + valid_ss; + + if (len(all_ss) > 0): + high_ss = max(all_ss); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/dpi2.py b/VNFs/DPPD-PROX/helper-scripts/dpi/dpi2.py new file mode 100644 index 00000000..65473f61 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/dpi2.py @@ -0,0 +1,229 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from testerset import * +from proxdpisut import * +from statsconsfile import * +from time import sleep +from time import time +from decimal import * +import copy +from os import system +import socket +from itertools import chain +from math import * +from csvwriter import * +from csvreader import * +from config import * +from progress import * +from resultprocessor import * + +def runTest(coreCount, testParam): + print "Running test with following parameters:" + print testParam.toString(); + + + testers = testerSet(config._test_systems, config._max_port_rate, testParam); + + ret = TestResult(testers.getCount()); + thresh = testParam.getConnections() * config._threshold; + converged = False; + + sut = ProxDpiSut(config._sut, coreCount); + + testers.startFork(); + sut.startFork(); + testers.startJoin(); + sut.startJoin(); + testers.wait_links_up(); + sut.startAllCores(); + sut.waitCmdFinished(); + testers.start_cores(); + + ret.addTimeTS(testers.getTsc()); + ret.addTimeSUT(sut.getTsc()); + + print "Running until convergence (%s connections)" % str(thresh) + p = Progress(thresh, ["connections", "setup rate", "reTX"], False); + while (not converged): + sleep(config._interCheckDuration) + testers.update_stats(); + + tot = testers.get_total_connections(); + tot_retx = testers.get_total_retx(); + rates = testers.get_rates(); + cur_setup_rate = testers.get_setup_rate(); + ierrors = testers.getIerrors(); + converged = tot >= thresh; + + if (not converged and not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + sut.forceQuit(); + sut.killProx(); + testers.killProx(); + return None; + + if (sut.getIerrors() != 0): + testers.killProx(); + print "Sending quit" + try: + sut.forceQuit(); + except: + print "Sending quit failed" + sut.killProx(); + return None; + + if (config._debug): + p.setProgress(tot, [tot, cur_setup_rate, tot_retx]); + print p.toString(); + + skipTime = config._skipTime + print "Connection threshold reached, waiting for " + str(skipTime) + "s, conditions checked = " + str(config._checkConditions) + while (skipTime > 0): + skipTime -= config._interCheckDuration + sleep(config._interCheckDuration) + testers.update_stats(); + if (config._checkConditions and not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + sut.forceQuit(); + sut.killProx(); + testers.killProx(); + return False, []; + + ret.addTimeTS(testers.getTsc()); + ret.addTimeSUT(sut.getTsc()); + + testers.tx_rate_meassurement(); + + testLength = config._testLength + print "Waiting final " + str(testLength) + "s" + while (testLength > 0): + testLength -= config._interCheckDuration + testers.update_stats(); + if (not testers.conditionsGood()): + print "conditions are bad: " + testers.getReason(); + sut.forceQuit(); + sut.killProx(); + testers.killProx(); + return None; + + if (sut.getIerrors() != 0): + testers.killProx(); + print "Sending quit" + try: + sut.forceQuit(); + except: + print "Sending quit failed" + sut.killProx(); + return None; + + sleep(config._interCheckDuration) + + rates = testers.tx_rate_meassurement(); + ret.addTimeTS(testers.getTsc()); + ret.addTimeSUT(sut.getTsc()); + + print "Quiting Prox on SUT" + # make sure stats are flushed + sut.quitProx(); + print "Quiting Prox on test system(s)" + testers.quitProx() + + ret.rates = rates + + sutStatsDump = "stats_dump_sut" + tsStatsDumpBaseName = "stats_dump_ts" + + sut.scpStatsDump(sutStatsDump); + tsStatsDump = testers.scpStatsDump(tsStatsDumpBaseName); + + ret.setTSStatsDump(tsStatsDump); + ret.setSUTStatsDump(sutStatsDump); + return ret + +def meassurePerf(coreCount, maxSetupRate, total_connections, ss_hi): + iterationCount = 0; + accuracy = 10**config._accuracy + ss_lo = 1 + ss_hi = int(round(ss_hi * accuracy, 0)) + success = True; + + downrate = float(0) + highest_ss = 0 + iterationOverride = [ss_hi, ss_lo]; + while (ss_lo <= ss_hi): + if (iterationCount < len(iterationOverride)): + ss = iterationOverride[iterationCount] + else: + ss = (ss_lo + ss_hi)/2; + + testParam = TestParameters(maxSetupRate, total_connections, float(ss)/accuracy); + + result = runTest(coreCount, testParam); + + if (result is None): + success = False + else: + rp = ResultProcessor(result) + rp.process(); + success = rp.percentHandled() > 0.99999 + + print "test result = " + str(success) + if (success): + ss_lo = ss + 1; + highest_ss = max(highest_ss, ss); + print result.rates + downrate = sum(result.rates)/len(result.rates) + else: + ss_hi = ss - 1; + iterationCount += 1 + + return downrate, float(highest_ss)/accuracy + +config = Config(); +config.parse(sys.argv[0], sys.argv[1:]) + +err = config.getErrorTestTwo(); +if (err is not None): + print "Invalid configuration: " + err; + exit(-1); +else: + print config.toString() + +infileFields = [] +infileFields += [("msr", "int")] +infileFields += [("conn", "int")] +infileFields += [("ss", "Decimal")] +infileFields += [("bw", "Decimal")] + +infile = CsvReader(infileFields); +infile.open(config.getInputFileName()) +inputs = infile.readAll() +infile.close(); + +summary = CsvWriter(); +summary.open(config._output_file_name); + +print "Will test up SUT config with " + str(config._dpiCoreList) + " DPI cores" + +for a in inputs: + for coreCount in config._dpiCoreList: + downrate, ss = meassurePerf(coreCount, a["msr"], a["conn"], a["ss"]); + summary.write([coreCount, a["msr"], a["conn"], ss, downrate]); + +summary.close() diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/maketable.py b/VNFs/DPPD-PROX/helper-scripts/dpi/maketable.py new file mode 100644 index 00000000..f8b7bdc0 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/maketable.py @@ -0,0 +1,140 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import sys +from config import * +from csvreader import * +from sets import Set +from csvwriter import * + +class ResultEntry: + def __init__(self): + self.boundary = None; + self.cores = {} + + def setBoundary(self, val): + self.boundary = val; + + def addCoreResult(self, core, val): + self.cores[core] = val + + def getCoreResult(self, core): + if (core in self.cores): + return self.cores[core]; + return None; + + def getBoundary(self): + return self.boundary; + + def getCores(self): + return self.cores + + def getMsr(self): + return self.msr; + +class DictEntry: + def __init__(self, key): + self.dictionary = {} + self.entries = [] + self.key = key; + +config = Config(); +config.parse(sys.argv[0], sys.argv[1:]) + +err = config.getErrorMakeTable(); + +if (err is not None): + print err + exit(-1); + +if (config._debug): + print "Performance data: " + config.getInputFileName2() + print "Boundaries: " + config.getInputFileName() + +allData = {} + +infileFields = [] +infileFields += [("msr", "int")] +infileFields += [("conn", "int")] +infileFields += [("ss", "Decimal")] +infileFields += [("bw", "Decimal")] + +boundariesFile = CsvReader(infileFields) +boundariesFile.open(config.getInputFileName()); +boundaries = boundariesFile.readAll(); + +cores = Set() + +orderedResults = [] +finalResults = {} + +for a in boundaries: + key = a["conn"] + if (key not in finalResults): + newDict = DictEntry(key) + finalResults[key] = newDict + orderedResults.append(newDict) + +for a in boundaries: + table = finalResults[a["conn"]] + key = a["msr"] + value = ResultEntry() + value.msr = a["msr"] + value.conn = a["conn"] + value.boundary = a["bw"] + table.dictionary[key] = value + table.entries.append(value) + +infileFields2 = [] +infileFields2 += [("cores", "int")] +infileFields2 += [("msr", "int")] +infileFields2 += [("conn", "int")] +infileFields2 += [("ss", "Decimal")] +infileFields2 += [("down", "Decimal")] + +resultsFile = CsvReader(infileFields2) +resultsFile.open(config.getInputFileName2()) + +for a in resultsFile.readAll(): + table = finalResults[a["conn"]] + key = a["msr"] + table.dictionary[key].addCoreResult(a["cores"], a["down"]) + cores.add(a["cores"]); + + +outputFile = CsvWriter() + +outputFile.open(config._output_file_name) + +title = ["setup rate", "maximum"] +for e in sorted(cores): + title += [str(e)] + +for a in orderedResults: + outputFile.write(["connections = " + str(a.key)]) + outputFile.write(title) + + for e in a.entries: + line = [str(e.getMsr())] + line += [str(e.getBoundary())] + for c in sorted(cores): + if (e.getCoreResult(c) is not None): + line += [str(e.getCoreResult(c))] + else: + line += [""] + outputFile.write(line) diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/progress.py b/VNFs/DPPD-PROX/helper-scripts/dpi/progress.py new file mode 100644 index 00000000..5e44c678 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/progress.py @@ -0,0 +1,67 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from decimal import * +from time import time + +class Progress: + def __init__(self, limit, fieldNames = [], overallETA = True): + self._fieldNames = fieldNames; + self._limit = limit; + self._progress = 0; + self._prevProgress = 0; + self._prevTime = 0; + self._progressSetCount = 0; + self._time = 0; + self._overallETA = overallETA; + + def setProgress(self, progress, fieldValues = []): + self._fieldValues = fieldValues; + if (self._overallETA == True): + self._progress = progress + self._time = time(); + if (self._progressSetCount == 0): + self._prevProgress = self._progress; + self._prevTime = self._time; + else: + self._prevProgress = self._progress; + self._prevTime = self._time; + self._progress = progress; + self._time = time(); + self._progressSetCount += 1 + + def incrProgress(self): + self.setProgress(self._progress + 1); + + def toString(self): + ret = "" + ret += str(self._getETA()) + " seconds left" + for f,v in zip(self._fieldNames, self._fieldValues): + ret += ", %s=%s" % (str(f),str(v)) + return ret; + + def _getETA(self): + if (self._progressSetCount < 2): + return "N/A" + diff = self._progress - self._prevProgress; + t_diff = Decimal(self._time - self._prevTime); + if (t_diff < 0.001 or diff <= 0): + return "N/A" + rate = Decimal(diff)/t_diff + remaining = Decimal(self._limit - self._progress); + return round(remaining/rate, 2); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/prox.py b/VNFs/DPPD-PROX/helper-scripts/dpi/prox.py new file mode 100644 index 00000000..60ef7592 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/prox.py @@ -0,0 +1,253 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import threading +from time import * +from proxsocket import * +from remotesystem import * + +class ProxStarter: + def __init__(self, remoteSystem, cmd): + self._remoteSystem = remoteSystem + self._cmd = cmd + self._thread = None + self._prox = None; + self._result = None; + self._startDuration = None + + def startThreaded(self): + self._start_thread = threading.Thread(target = self._run, args = (self, 1)) + self._start_thread.start(); + + def joinThreaded(self): + self._start_thread.join(); + return self._result; + + def getResult(self): + return self._result; + + def getStartDuration(self): + return self._startDuration; + def getProx(self): + return self._prox; + + def _run(self, a, b): + before = time.time() + self._remoteSystem.run("sudo killall -w -q -9 prox") + + self._result = self._remoteSystem.run(self._cmd); + + sleep(1) + after = time.time() + self._startDuration = after - before; + +class StatsCmd(object): + def __init__(self, prox): + self._cmd = "" + self._parts = [] + self._beforeParts = [] + self._prox = prox; + + def sendRecv(self): + cmd = self.getCmd() + reply = self._prox._send(cmd)._recv() + self.setReply(reply) + + def add(self, stats): + if (len(self._cmd) != 0): + self._cmd += "," + self._cmd += stats + + if (len(self._parts) == 0): + self._beforeParts += [0] + else: + before = self._parts[-1] + self._beforeParts[-1]; + self._beforeParts += [before] + + self._parts += [stats.count(",") + 1]; + + def getCmd(self): + return "stats " + self._cmd; + + def setReply(self, reply): + self._reply = reply.split(","); + + def getResult(self, idx): + start = self._beforeParts[idx]; + end = start + self._parts[idx]; + return self._reply[start:end] + +class Prox(object): + def __init__(self, systemConfig): + self._systemConfig = systemConfig; + self._proxStarter = None + + user = self._systemConfig._user + ip = self._systemConfig._ip + self._remoteSystem = remoteSystem(user, ip); + + self.resetArguments() + + def resetArguments(self): + self._args = [] + + def addArgument(self, arg): + self._args.append(arg); + + def startFork(self): + cmd = self.getCmd(); + self._proxStarter = ProxStarter(self._remoteSystem, cmd) + self._proxStarter.startThreaded(); + + def startJoin(self): + ret = self.startJoinNoConnect(); + self._connectSocket(); + self._querySetup(); + return self._proxStarter.getStartDuration(); + + def startJoinNoConnect(self): + return self._proxStarter.joinThreaded(); + + def getCmd(self): + proxDir = self._systemConfig.getProxDir(); + cfgFile = self._systemConfig.getCfgFile(); + + cmd = "cd " + proxDir + "; " + cmd += "sudo ./build/prox " + cmd += "-f " + cfgFile + + for arg in self._args: + cmd += " " + arg + return cmd + + def getLog(self): + proxDir = self._systemConfig.getProxDir() + cmd = "cat " + proxDir + "/prox.log"; + return self._remoteSystem.run(cmd)["out"]; + + def getIP(self): + return self._systemConfig._ip; + + def getHz(self): + return self._hz; + + def getBeg(self): + return self._beg; + + def getPorts(self): + return self._ports; + + def getIerrors(self): + sc = StatsCmd(self) + sc.add(self._buildIerrorsCmd()); + sc.sendRecv() + return self._parseIerrorsReply(sc.getResult(0)); + + def _parseIerrorsReply(self, rep): + tot_ierrors = 0; + for e in rep: + tot_ierrors += int(e); + return tot_ierrors; + + def _buildIerrorsCmd(self): + cmd = "" + for port in self._ports: + if (len(cmd)): + cmd += "," + cmd += "port(%s).ierrors" % str(port) + return cmd; + + def waitCmdFinished(self): + self._send("stats hz")._recv(); + + def waitAllLinksUp(self): + link_down = True; + while (link_down): + link_down = False; + for port in self._ports: + cmd = "port link state %s" % str(port) + link_state = self._send(cmd)._recv(); + if (link_state == "down"): + link_down = True; + print "Link down on port " + str(port) + ", waiting one second" + break; + sleep(1); + + def startAllCores(self): + self._send("start all"); + + def stopAllCores(self): + self._send("stop all"); + + def forceQuit(self): + self._send("quit_force")._recv(); + + def killProx(self): + self._remoteSystem.run("sudo killall -w -q -9 prox") + + def getTsc(self): + return self._getTsc(); + + def _getTsc(self): + return int(self._send("stats global.tsc")._recv()); + + def scpStatsDump(self, dst): + proxDir = self._systemConfig.getProxDir() + + src = proxDir + "/stats_dump"; + print "Copying " + src + " to " + dst + self._remoteSystem.scp(src, dst); + + def _querySetup(self): + print "Query setup on " + str(self.getIP()) + self._queryHz() + self._queryBeg() + self._queryPorts() + self._querySetup2() + + def _querySetup2(self): + print "running query 2" + pass + + def quitProx(self): + self._send("quit")._recv(); + + def _queryHz(self): + self._hz = int(self._send("stats hz")._recv()); + + def _queryBeg(self): + self._beg = self._getTsc(); + + def _queryPorts(self): + self._ports = [] + port_info_all = self._send("port info all")._recv(); + port_info_list = port_info_all.split(','); + + for port_info in port_info_list: + if (len(port_info) > 0): + self._ports.append(int(port_info.split(":")[0])); + + def _connectSocket(self): + self._proxSocket = ProxSocket(self.getIP()) + + def _send(self, msg): + self._proxSocket.send(msg); + return self + + def _recv(self): + return self._proxSocket.recv(); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpisut.py b/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpisut.py new file mode 100644 index 00000000..aae900b0 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpisut.py @@ -0,0 +1,61 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from prox import * +from remotesystem import * +from time import * +from decimal import * + +class ProxDpiSut(Prox): + def __init__(self, ts, coreCount): + super(ProxDpiSut, self).__init__(ts) + + self._setDefaultArguments(); + self._setDpiCoreCount(coreCount); + + def _setDefaultArguments(self): + self.addArgument("-e"); + self.addArgument("-t"); + self.addArgument("-k"); + self.addArgument("-d"); + self.addArgument("-r 0.01"); + + def _setDpiCoreCount(self, count): + self.addArgument("-q dpi_core_count=" + str(count)) + + def _querySetup2(self): + self._query_cores(); + + def _query_cores(self): + print "querying cores" + self._wk = self._get_core_list("$wk"); + + def _get_core_list(self, var): + ret = [] + result = self._send("echo " + var)._recv(); + for e in result.split(","): + ret += [e]; + return ret; + + def getTsc(self): + cmd = "stats task.core(%s).task(0).tsc" % self._wk[-1] + res = int(self._send(cmd)._recv()); + if (res == 0): + return self._getTsc(); + else: + return res; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpitester.py b/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpitester.py new file mode 100644 index 00000000..19b08c92 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/proxdpitester.py @@ -0,0 +1,258 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from prox import * +from remotesystem import * +from time import * +from decimal import * +from timeseriespoint import * + +class TestParameters: + def __init__(self, max_setup_rate, total_connections, ss): + self.max_setup_rate = max_setup_rate; + self.total_connections = total_connections; + self.ss = ss; + + def toString(self): + ret = "" + ret += "\tMaximum setup rate = %d\n" % self.max_setup_rate + ret += "\tTotal number of connections = %d\n" % self.total_connections + ret += "\tSpeed scaling = %s\n" % str(self.ss) + return ret; + + def getPerSystem(self, count): + msr = self.max_setup_rate / count + cnn = self.total_connections / count + return TestParameters(msr, cnn, self.ss); + + def getConnections(self): + return self.total_connections; + +class ProxDpiTester(Prox): + TENGIGABITBYTESPERSECOND = 1250000000 + + def __init__(self, ts, testParam, ID): + super(ProxDpiTester, self).__init__(ts) + + self._sc = None + self._lastTot = None + self._prevTot = None; + self._prevBytesClient = None + self._lastBytesClient = None + self._prevBytesTxMeassurement = None + self._lastBytesTxMeassurement = None + + self._setDefaultArguments(); + self._setMsr(testParam.max_setup_rate) + self._setConnections(testParam.total_connections); + self._setSpeedScaling(testParam.ss); + self._setID(ID); + + def _setDefaultArguments(self): + self.addArgument("-e") + self.addArgument("-t") + self.addArgument("-k") + self.addArgument("-d") + self.addArgument("-r 0.01"); + + def _setMsr(self, msr): + self.addArgument("-q max_setup_rate=" + str(msr)) + + def _setConnections(self, connections): + self.addArgument("-q connections=" + str(connections)) + + def _setID(self, ID): + self.addArgument("-q test_system_id=" + str(ID)) + + def _setSpeedScaling(self, ss): + self.addArgument("-q ss=" + str(ss)) + + def _querySetup2(self): + self._query_client_ports(); + self._query_server_ports(); + self._query_cores(); + + def _query_client_ports(self): + self._client_ports = [] + for i in range(0, len(self._ports), 2): + self._client_ports.append(self._ports[i]); + + def _query_server_ports(self): + self._server_ports = [] + for i in range(1, len(self._ports), 2): + self._server_ports.append(self._ports[i]); + + def _query_cores(self): + self._query_ld(); + self._query_servers(); + self._query_clients(); + + def _query_ld(self): + self._ld = self._get_core_list("$all_ld"); + + def _query_servers(self): + self._servers = self._get_core_list("$all_servers") + + def _query_clients(self): + self._clients = self._get_core_list("$all_clients") + + def _get_core_list(self, var): + ret = [] + result = self._send("echo " + var)._recv(); + for e in result.split(","): + ret += [e]; + return ret; + + def start_all_ld(self): + self._send("start $all_ld"); + + def start_all_workers(self): + self._send("start $all_workers"); + + def stop_all_ld(self): + self._send("stop $all_ld"); + + def stop_all_workers(self): + self._send("stop $all_workers"); + + def update_stats(self): + if (self._sc is None): + self._sc = StatsCmd(self) + self._sc.add(self._buildTotalConnectionsCmd()) + self._sc.add(self._buildReTXCmd()) + self._sc.add(self._buildIerrorsCmd()) + self._sc.add(self._buildBytesPerPortCmd(self._client_ports, "rx")); + + self._sc.sendRecv() + + self._updateTotalConnections(self._sc.getResult(0)) + self._updateReTX(self._sc.getResult(1)) + self._updateIerrors(self._sc.getResult(2)) + self._update_rates_client_ports(self._sc.getResult(3)); + + def _buildTotalConnectionsCmd(self): + cmd = "l4gen(%s).tsc" % str(self._clients[0]) + + for core in self._clients: + if (len(cmd) > 0): + cmd += "," + cmd += "l4gen(%s).created,l4gen(%s).finished" % (str(core), str(core)) + return cmd; + + def _updateTotalConnections(self, rep): + instant = Decimal(int(rep[0]) - self._beg)/self._hz + rep = rep[1:] + tot = 0; + for i in range(0,len(rep), 2): + tot += int(rep[i]) - int(rep[i + 1]); + + prev = self._lastTot; + last = TimeSeriesPoint(tot, instant); + + if (prev == None): + prev = last; + + self._prevTot = prev + self._lastTot = last; + + def _buildReTXCmd(self): + cmd = "" + for core in self._clients + self._servers: + if (len(cmd) > 0): + cmd += "," + cmd += "l4gen(%s).retx" % str(core) + return cmd; + + def _updateReTX(self, rep): + retx = 0; + for i in rep: + retx += int(i); + self._retx = retx; + + def _updateIerrors(self, rep): + self._ierrors = self._parseIerrorsReply(rep) + + def get_total_connections(self): + return self._lastTot.getValue() + + def getCurrentSetupRate(self): + return int(self._lastTot.getRateOfChange(self._prevTot)); + + def get_total_retx(self): + return self._retx + + def get_rates_client_ports(self): + return self._calcLinkUtilization(self._prevBytesClient, self._lastBytesClient); + + def getIerrorsCached(self): + return self._ierrors; + + def _update_rates_client_ports(self, rep): + prevBytes = self._lastBytesClient + lastBytes = self._parseTimeSeries(rep); + + if (prevBytes == None): + prevBytes = lastBytes; + + self._prevBytesClient = prevBytes; + self._lastBytesClient = lastBytes; + + def _getBytesPerPort(self, ports, rxOrTx): + sc = StatsCmd(self); + sc.add(self._buildBytesPerPortCmd(ports, rxOrTx)) + sc.sendRecv(); + + rep = sc.getResult(0); + + return self._parseTimeSeries(rep); + + def _buildBytesPerPortCmd(self, ports, rxOrTx): + cmd = "" + for port in ports: + if (len(cmd) > 0): + cmd += "," + cmd += "port(%s).%s.bytes,port(%s).tsc" % (str(port), rxOrTx, str(port)); + return cmd + + def tx_rate_meassurement(self): + prev = self._lastBytesTxMeassurement + last = self._getBytesPerPort(self._server_ports, "tx"); + + if (prev == None): + prev = last; + + self._prevBytesTxMeassurement = prev + self._lastBytesTxMeassurement = last + + return self._calcLinkUtilization(prev, last); + + def _parseTimeSeries(self, rep): + ret = [] + for i in range(0, len(rep), 2): + val = int(rep[0]) + instant = Decimal(int(rep[1]) - self._beg)/self._hz + ret.append(TimeSeriesPoint(val, instant)); + return ret; + + def _calcLinkUtilization(self, prev, last): + ret = [] + for i in range(0, len(prev)): + bytesPerSecond = last[i].getRateOfChange(prev[i]); + linkFraction = Decimal(bytesPerSecond)/self.TENGIGABITBYTESPERSECOND + ret.append(round(linkFraction,2)); + return ret; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/proxmaxssprobe.py b/VNFs/DPPD-PROX/helper-scripts/dpi/proxmaxssprobe.py new file mode 100644 index 00000000..27c470c4 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/proxmaxssprobe.py @@ -0,0 +1,34 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from decimal import * +from prox import * + +class ProxMaxSSProbe(Prox): + def __init__(self, ts): + super(ProxMaxSSProbe, self).__init__(ts) + + def getMaxSS(self): + self.addArgument("-q max_ss_and_quit=true"); + self.addArgument("-q test_system_id=0"); + self.startFork(); + ret = self.startJoinNoConnect(); + last_occur = ret["out"].rfind("\n") + 1; + last_line = ret["out"][last_occur:]; + + return Decimal(last_line.split("=")[1]) diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/proxsocket.py b/VNFs/DPPD-PROX/helper-scripts/dpi/proxsocket.py new file mode 100644 index 00000000..fd4cc737 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/proxsocket.py @@ -0,0 +1,54 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import socket + +class ProxSocket: + def __init__(self, ip): + self._ip = ip; + self._dat = "" + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((self._ip, 8474)) + except: + raise Exception("Failed to connect to prox on " + self._ip) + self._sock = sock; + + def send(self, msg): + self._sock.sendall(msg + "\n"); + return self + + def recv(self): + ret_str = ""; + done = 0; + while done == 0: + if (len(self._dat) == 0): + self._dat = self._sock.recv(256); + if (self._dat == ''): + return ''; + + while(len(self._dat)): + if (self._dat[0] == '\n'): + done = 1 + self._dat = self._dat[1:] + break; + else: + ret_str += self._dat[0]; + self._dat = self._dat[1:] + return ret_str; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/ratedistribution.py b/VNFs/DPPD-PROX/helper-scripts/dpi/ratedistribution.py new file mode 100644 index 00000000..41d8ad53 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/ratedistribution.py @@ -0,0 +1,69 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import sys +from decimal import * + +def usage(progName): + print "usage: " + progName + " config [up|down]" + print " The script reads a lua configuration " + print " and outputs a histogram wit 21 buckets." + print " The first 20 buckets contain 70th percentile." + print " The last bucket contains the remaining items." + exit(-1); + +if (len(sys.argv) != 3): + usage(sys.argv[0]) + +if (sys.argv[2] == "down"): + match = "dn_bps" +elif (sys.argv[2] == "up"): + match = "up_bps" +else: + usage(sys.argv[0]) + +values = [] +for line in open(sys.argv[1]).readlines(): + line = line.strip(); + + if line.find(match) != -1: + v = line.split(" = ")[1].strip(",") + values.append(Decimal(v)); + +values = sorted(values) + +treshold = values[int(len(values)*0.7)] + +buckets = [0]*21; + +for v in values: + if (v > treshold): + buckets[20] += 1 + else: + buckets[int(v * 20 / treshold)] += 1 + +stepSize = treshold / 20; + +print "# bucket range, count" +for i in range(len(buckets) - 1): + beg = str(int(i * stepSize)) + end = str(int((i + 1) * stepSize - 1)) + print beg + "-" + end + "," + str(buckets[i]) + +i = len(buckets) - 1 +print beg + "+," + str(buckets[i]) diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/remotesystem.py b/VNFs/DPPD-PROX/helper-scripts/dpi/remotesystem.py new file mode 100644 index 00000000..adbb288c --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/remotesystem.py @@ -0,0 +1,58 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import os +import time +import socket + +def ssh(user, ip, cmd): + # print cmd; + ssh_options = "" + ssh_options += "-o StrictHostKeyChecking=no " + ssh_options += "-o UserKnownHostsFile=/dev/null " + ssh_options += "-o LogLevel=quiet " + running = os.popen("ssh " + ssh_options + " " + user + "@" + ip + " \"" + cmd + "\""); + ret = {}; + ret['out'] = running.read().strip(); + ret['ret'] = running.close(); + if (ret['ret'] == None): + ret['ret'] = 0; + + return ret; + +def ssh_check_quit(obj, user, ip, cmd): + ret = ssh(user, ip, cmd); + if (ret['ret'] != 0): + obj._err = True; + obj._err_str = ret['out']; + exit(-1); + +class remoteSystem: + def __init__(self, user, ip): + self._ip = ip; + self._user = user; + + def run(self, cmd): + return ssh(self._user, self._ip, cmd); + + def scp(self, src, dst): + running = os.popen("scp " + self._user + "@" + self._ip + ":" + src + " " + dst); + return running.close(); + + def getIP(self): + return self._ip diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/resultprocessor.py b/VNFs/DPPD-PROX/helper-scripts/dpi/resultprocessor.py new file mode 100644 index 00000000..ad196035 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/resultprocessor.py @@ -0,0 +1,210 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from sutstatsconsfile import * +from tsstatsconsfile import * +from csvwriter import * + +class TestResult: + class Times: + def __init__(self): + self.serie = [] + def addTime(self, val): + self.serie.append(val) + def getTime(self, i): + return self.serie[i] + + def __init__(self, testSystemCount): + self.rates = None; + self.tsStatsDump = []; + self.tsTimes = []; + for i in range(testSystemCount): + self.tsStatsDump.append(""); + self.tsTimes.append(TestResult.Times()); + + self.sutStatsDump = None; + self.sutTime = TestResult.Times(); + + def getTSCount(self): + return len(self.tsTimes) + + def setTSStatsDump(self, filePaths): + self.tsStatsDump = filePaths; + + def setSUTStatsDump(self, filePath): + self.sutStatsDump = filePath; + + def getTSStatsDump(self): + return self.tsStatsDump; + + def getSUTStatsDump(self): + return self.sutStatsDump; + + def addTimeTS(self, times): + for i in range(len(times)): + self.tsTimes[i].addTime(times[i]) + + def addTimeSUT(self, time): + self.sutTime.addTime(time); + + +class ResultProcessor: + def __init__(self, testResult): + self._testResults = testResult; + + def process(self): + self._readStatsConsLogs(); + self._mergeTsStats(); + self._calcSetupRate(); + + def percentHandled(self): + converged_tsc = self._testResults.sutTime.getTime(1) - self._testResults.sutTime.getTime(0) + end_tsc = self._testResults.sutTime.getTime(2) - self._testResults.sutTime.getTime(0) + + converged = converged_tsc/Decimal(self._sutHz) + end = end_tsc/Decimal(self._sutHz); + + rx_converged = -1 + tx_converged = -1 + rx_end = -1 + tx_end = -1 + + for entry in self._sutStats: + timeStamp = entry[3] + if (rx_converged == -1): + if (timeStamp > converged): + rx_converged = entry[0] + tx_converged = entry[1] - entry[2] + else: + continue; + else: + if (timeStamp > end): + rx_end = entry[0] + tx_end = entry[1] - entry[2] + break; + return (tx_end - tx_converged)/Decimal(rx_end - rx_converged) + + def toFile(self, fileName): + outFile = CsvWriter(); + + outFile.open(fileName) + + for entry in self._sutStats: + timeStamp = round(entry[3], 3); + rx = entry[0] + tx = entry[1] + drop = entry[2] + + outFile.write([timeStamp, rx, tx, drop, "", ""]) + + for entry in self._tsStats: + timeStamp = round(entry[-1], 3); + connections = entry[0] + setupRate = entry[3] + outFile.write([timeStamp,"","","", connections, setupRate]); + outFile.close(); + + def _readStatsConsLogs(self): + print "Reading SUT stats" + self._sutStats = self._readSutStats(); + print "Reading TS stats" + self._tsAllStats = self._readAllTSStats(); + + def _mergeTsStats(self): + # The first test system is the reference system. The totals + # will be accumulated by repeatedly taking the closest + # available data from other systems + ret = [] + for entry in self._tsAllStats[0]: + ret.append(entry) + + interSampleTime = ret[1][-1] - ret[0][-1]; + + mergedSampleCount = 0; + if (len(self._tsAllStats) == 1): + mergedSampleCount = len(ret) + + for i in range(0, len(self._tsAllStats) - 1): + prev = 0; + for entry in ret: + timeStamp = entry[-1] + found = False; + + for idx in range(prev, len(self._tsAllStats[i])): + diff = abs(self._tsAllStats[i][idx][-1] - timeStamp) + if (diff < interSampleTime): + found = True; + prev = idx; + break; + + if (found): + entry[0] += self._tsAllStats[i][prev][0] + entry[1] += self._tsAllStats[i][prev][1] + mergedSampleCount += 1; + else: + break; + + self._tsStats = ret[0: mergedSampleCount]; + + def _calcSetupRate(self): + for i in range(0, len(self._tsStats)): + prevCreated = 0 + prevTime = 0 + if (i > 0): + prevCreated = self._tsStats[i - 1][1]; + prevTime = self._tsStats[i - 1][-1]; + curCreated = self._tsStats[i][1]; + curTime = self._tsStats[i][-1]; + + setupRate = (curCreated - prevCreated)/(curTime - prevTime) + + self._tsStats[i].append(setupRate); + + + def _readSutStats(self): + ret = [] + fileName = self._testResults.getSUTStatsDump(); + beg = self._testResults.sutTime.getTime(0); + f = SutStatsConsFile(fileName, beg); + entry = f.readNext(); + self._sutHz = f.getHz(); + while (entry is not None): + ret.append(entry); + entry = f.readNext(); + f.close(); + return ret; + + def _readAllTSStats(self): + stats = [] + for i in range(self._testResults.getTSCount()): + fileName = self._testResults.getTSStatsDump()[i] + beg = self._testResults.tsTimes[i].getTime(0) + tsStat = self._readTSStats(fileName, beg) + stats.append(tsStat); + return stats; + + def _readTSStats(self, fileName, beg): + ret = [] + f = TSStatsConsFile(fileName, beg) + + entry = f.readNext() + while (entry is not None): + ret.append(entry); + entry = f.readNext(); + f.close() + return ret; diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/statsconsfile.py b/VNFs/DPPD-PROX/helper-scripts/dpi/statsconsfile.py new file mode 100644 index 00000000..a25c1232 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/statsconsfile.py @@ -0,0 +1,84 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import os +import struct + +class StatsConsFile: + def __init__(self, file_name, tsc = None): + self._file = open(file_name, "rb"); + try: + data = self._file.read(4*8); + dataUnpacked = struct.unpack("<qqqq", data); + + self._hz = dataUnpacked[0] + if (tsc is None): + self._tsc = dataUnpacked[1] + else: + self._tsc = tsc; + + self._entryCount = dataUnpacked[2] + fieldCount = dataUnpacked[3] + + data = self._file.read(fieldCount); + fmt = "b" * fieldCount; + + dataUnpacked = struct.unpack("<" + fmt, data); + self._entryFmt = "<"; + self._entrySize = 0; + + for e in dataUnpacked: + if (e == 4): + self._entryFmt += "i" + elif (e == 8): + self._entryFmt += "q" + else: + raise Exception("Unknown field format: " + str(e)) + self._entrySize += e + except: + print "except" + self._file.close(); + + def setBeg(self, tsc): + self._tsc = tsc + + def getBeg(self): + return self._tsc; + + def getHz(self): + return self._hz + + def readNext(self): + ret = [] + for i in range(self._entryCount): + entry = self._readNextEntry() + if (entry == None): + return None; + ret.append(entry); + return ret; + + def _readNextEntry(self): + try: + entry = self._file.read(self._entrySize); + entryUnpacked = struct.unpack(self._entryFmt, entry); + return list(entryUnpacked) + except: + return None; + + def close(self): + self._file.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/sutstatsconsfile.py b/VNFs/DPPD-PROX/helper-scripts/dpi/sutstatsconsfile.py new file mode 100644 index 00000000..82bca9a8 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/sutstatsconsfile.py @@ -0,0 +1,61 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from statsconsfile import * +from decimal import * + +class SutStatsConsFile: + def __init__(self, fileName, offset): + self.offset = offset; + self.statsConsFile = StatsConsFile(fileName) + + def readNext(self): + entry = self._readNextEntry(); + + if (entry is None): + return None; + + while (entry is not None and entry[-1] <= 0): + entry = self._readNextEntry(); + return entry; + + def getHz(self): + return self.statsConsFile.getHz(); + + def _readNextEntry(self): + entry = self.statsConsFile.readNext(); + if (entry is None): + return None; + + rx = 0; + tx = 0; + drop = 0; + last_tsc = 0; + + for i in range(0, len(entry), 2): + rx += entry[i][2] + tx += entry[i][3] + drop += entry[i][4] + last_tsc = entry[i][5] + + last_tsc -= self.offset; + last_tsc = Decimal(last_tsc) / self.statsConsFile.getHz(); + return [rx, tx, drop, last_tsc]; + + def close(self): + self.statsConsFile.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/systemconfig.py b/VNFs/DPPD-PROX/helper-scripts/dpi/systemconfig.py new file mode 100644 index 00000000..9e35576f --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/systemconfig.py @@ -0,0 +1,73 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +class SystemConfig: + _user = None + _ip = None + _proxDir = None + _cfgFile = None + def __init__(self, user, ip, proxDir, configDir): + self._user = user; + self._ip = ip; + self._proxDir = proxDir; + self._cfgFile = configDir; + def __init__(self, text): + self._user = text.split("@")[0]; + text = text.split("@")[1]; + self._ip = text.split(":")[0]; + self._proxDir = text.split(":")[1]; + self._cfgFile = text.split(":")[2]; + + def getUser(self): + return self._user; + + def getIP(self): + return self._ip; + + def getProxDir(self): + return self._proxDir; + + def getCfgFile(self): + return self._cfgFile; + + @staticmethod + def checkSyntax(text): + split = text.split("@"); + if (len(split) != 2): + return SystemConfig.getSyntaxError(text); + after = split[1].split(":"); + if (len(after) != 3): + return SystemConfig.getSyntaxError(text); + return "" + def toString(self): + ret = ""; + ret += " " + self._user + "@" + self._ip + "\n" + ret += " " + "prox dir: " + self._proxDir + "\n" + ret += " " + "cfg dir: " + self._cfgFile + "\n" + return ret; + + @staticmethod + def getSyntaxError(text): + ret = "Invaild system syntax" + ret += ", got: " + str(text) + ret += ", expected: " + str(SystemConfig.expectedSyntax()) + return ret; + + @staticmethod + def expectedSyntax(): + return "user@ip:proxDir:cfgFile" diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/testerset.py b/VNFs/DPPD-PROX/helper-scripts/dpi/testerset.py new file mode 100644 index 00000000..fe3dce72 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/testerset.py @@ -0,0 +1,176 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from proxdpitester import * + +class testerSet: + def __init__(self, test_systems, maxRate, testParam): + self._test_systems = []; + self._reason = "" + self._maxRate = maxRate + + testParamPerSystem = testParam.getPerSystem(len(test_systems)); + + for i in range(len(test_systems)): + ts = test_systems[i]; + to_add = ProxDpiTester(ts, testParamPerSystem, i); + self.add_test_system(to_add); + + def getCount(self): + return len(self._test_systems); + + def add_test_system(self, test_system): + self._test_systems.append(test_system); + + def startFork(self): + print "Starting test systems:" + for ts in self._test_systems: + print "\t" + str(ts.getIP()) + ts.startFork(); + + def startJoin(self): + for ts in self._test_systems: + elapsed = ts.startJoin(); + if (elapsed == None): + print "Failed to start on " + str(ts.getIP()) + else: + print "Started on " + str(ts.getIP()) + sleep(1); + + def startForkJoin(self): + self.startFork(); + self.startJoin(); + + def update_stats(self): + for ts in self._test_systems: + ts.update_stats(); + + def wait_links_up(self): + for ts in self._test_systems: + ts.waitAllLinksUp(); + sleep(1); + + def start_cores(self): + for ts in self._test_systems: + ts.start_all_ld(); + ts.waitCmdFinished(); + for ts in self._test_systems: + ts.start_all_workers(); + for ts in self._test_systems: + ts.waitCmdFinished(); + + def stop_cores(self): + for ts in self._test_systems: + ts.stop_all_workers(); + ts.stop_all_ld(); + + for ts in self._test_systems: + ts.waitCmdFinished(); + + def getTsc(self): + ret = [] + for ts in self._test_systems: + ret += [ts.getTsc()] + return ret; + + def get_setup_rate(self): + total = 0; + for ts in self._test_systems: + total += ts.getCurrentSetupRate(); + return total + + def get_total_connections(self): + total = 0; + for ts in self._test_systems: + ts_tot_conn = ts.get_total_connections(); + total += ts_tot_conn + + return total; + + def get_total_retx(self): + total = 0; + for ts in self._test_systems: + total += ts.get_total_retx(); + return total; + + def getIerrors(self): + total = 0; + for ts in self._test_systems: + total += ts.getIerrorsCached(); + return total; + + def get_rates(self): + rates = []; + for ts in self._test_systems: + rates += ts.get_rates_client_ports(); + return rates; + + def tx_rate_meassurement(self): + rates = [] + for ts in self._test_systems: + rates += ts.tx_rate_meassurement(); + return rates; + + def scpStatsDump(self, dst): + ret = [] + for i in range(len(self._test_systems)): + dstFileName = dst + str(i); + ret.append(dstFileName); + self._test_systems[i].scpStatsDump(dstFileName) + return ret; + + def conditionsGood(self): + tot_retx = self.get_total_retx(); + rates = self.get_rates(); + ierrors = self.getIerrors(); + + if (tot_retx > 100): + self._reason = "Too many reTX (" + str(tot_retx) + ")" + return False; + if (ierrors > 0): + self._reason = "Too many ierrors (" + str(ierrors) + ")" + return False; + for i in range(0, len(rates)): + if (rates[i] > self._maxRate): + self._setReason(i, rates) + return False; + return True; + + def _setReason(self, port, rates): + portStr = str(port); + rateStr = str(rates[port]) + maxRateStr = str(self._maxRate); + allRatesStr = str(rates); + + fmt = "Rate on port %s = %s > %s, rate on all = %s" + self._reason = fmt % (portStr, rateStr, maxRateStr, allRatesStr) + + def getReason(self): + return self._reason; + + def quitProx(self): + for ts in self._test_systems: + ts.quitProx(); + + def killProx(self): + for ts in self._test_systems: + ts.stop_all_workers(); + for ts in self._test_systems: + ts.stop_all_ld(); + for ts in self._test_systems: + ts.killProx(); diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/timeseriespoint.py b/VNFs/DPPD-PROX/helper-scripts/dpi/timeseriespoint.py new file mode 100644 index 00000000..521a0893 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/timeseriespoint.py @@ -0,0 +1,39 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from decimal import * + +class TimeSeriesPoint: + def __init__(self, value, instant): + self._value = value; + self._instant = instant; + + def getValue(self): + return self._value; + + def getInstant(self): + return self._instant; + + def getRateOfChange(self, other): + diff = self.getValue() - other.getValue(); + t_diff = self.getInstant() - other.getInstant(); + + if (diff == 0 or abs(t_diff) <= 0.00001): + return Decimal(0) + else: + return Decimal(diff)/t_diff diff --git a/VNFs/DPPD-PROX/helper-scripts/dpi/tsstatsconsfile.py b/VNFs/DPPD-PROX/helper-scripts/dpi/tsstatsconsfile.py new file mode 100644 index 00000000..10e48a68 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/dpi/tsstatsconsfile.py @@ -0,0 +1,60 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from statsconsfile import * +from decimal import * + +class TSStatsConsFile: + def __init__(self, fileName, offset): + self.offset = offset; + self.statsConsFile = StatsConsFile(fileName) + + def readNext(self): + entry = self._readNextEntry(); + if (entry is None): + return None; + + while (entry is not None and entry[-1] <= 0): + entry = self._readNextEntry(); + + return entry; + + def _readNextEntry(self): + entry = self.statsConsFile.readNext(); + if (entry is None): + return None; + + rx = 0; + tx = 0; + active = 0; + created = 0; + last_tsc = 0; + for i in range(0, len(entry), 2): + active += entry[i][2] + created += entry[i][3] + rx += entry[i][4] + tx += entry[i][5] + last_tsc = entry[i][6] + + last_tsc -= self.offset; + last_tsc = Decimal(last_tsc) / self.statsConsFile.getHz(); + + return [active, created, rx, tx, last_tsc]; + + def close(self): + self.statsConsFile.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/gen_4over6.pl b/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/gen_4over6.pl new file mode 100755 index 00000000..8e42eeba --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/gen_4over6.pl @@ -0,0 +1,271 @@ +#!/usr/bin/perl + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +use strict vars; +use Getopt::Long; +use Pod::Usage; +use Net::Pcap; +use Net::Frame::Layer; +use Net::Frame::Layer::ETH qw(:consts); +use Net::Frame::Layer::IPv6 qw(:consts); +use Net::Frame::Layer::IPv4 qw(:consts); +use Net::Frame::Layer::UDP; +use Socket qw(AF_INET AF_INET6 inet_ntop inet_pton); + +use constant NUM_PACKETS => 30000; + +use constant ETHER_ADDR_LEN => 6; +use constant ETHER_TYPE_LEN => 2; +use constant ETHER_HDR_LEN => ( 2 * ETHER_ADDR_LEN ) + ETHER_TYPE_LEN; +use constant ETHER_STATIC_MAC => "78acdddddddd"; + +use constant UDP_HDR_LEN => 8; +use constant UDP_STATIC_PORT => 0x6666; + +use constant IPv6_HOP_LIMIT => 4; +use constant IPv6_STATIC_IP => "2222:2222:2222:2222:2222:2222:2222:2222"; + +use constant IPv4_TIME_TO_LIVE => 32; +use constant IPv4_STATIC_IP => "68.68.68.68"; + +srand; + +my $type = 'tun'; +my $pkt_count = NUM_PACKETS; + +GetOptions( + 'inet' => sub { $type = 'inet'}, + 'tun' => sub { $type = 'tun'}, + 'count=i' => \$pkt_count, + 'in=s' => \(my $in = 'ip6_tun_bind.lua'), + 'out=s' => \(my $out = 'output.pcap'), + 'size=s' => \(my $size = 0) +) or exit; + +my $pcap = pcap_open_dead( DLT_EN10MB, 65535 ); +my $dumper = pcap_dump_open($pcap, $out ) or die 'Could not create output file: ' . $out; + +if( $type eq 'inet' ) { + gen_inet_pcap( $in, $pkt_count ); +} +if( $type eq 'tun' ) { + gen_tun_pcap( $in, $pkt_count ); +} + +pcap_close( $pcap ); + +# Trim string +sub trim { + my ( $str ) = @_; + + $str =~ s/^\s+|\s+$//g; + + return $str; +} + +# Generate random port based on $port and $port_mask +sub rand_port { + my ( $port, $port_mask ) = @_; + + return ( $port | int( rand( 0xFFFF ) & $port_mask ) ); +} + +# Generate packet originating from CPE +sub gen_tun_packet { + my ( $sz, $ether, $ipv6, $ipv4, $udp ) = @_; + + my $hdr_ether = Net::Frame::Layer::ETH->new( + src => $ether->{'src'}, + dst => $ether->{'dst'}, + type => NF_ETH_TYPE_IPv6 + )->pack; + + my $hdr_ipv6 = Net::Frame::Layer::IPv6->new( + nextHeader => NF_IPv6_PROTOCOL_IPIP, + hopLimit => IPv6_HOP_LIMIT, + src => $ipv6->{'src'}, + dst => $ipv6->{'dst'}, + payloadLength => $sz + NF_IPv4_HDR_LEN + UDP_HDR_LEN + )->pack; + + my $hdr_ipv4 = Net::Frame::Layer::IPv4->new( + length => $sz + UDP_HDR_LEN + NF_IPv4_HDR_LEN, + ttl => IPv4_TIME_TO_LIVE, + protocol => NF_IPv4_PROTOCOL_UDP, + src => $ipv4->{'src'}, + dst => $ipv4->{'dst'} + )->pack; + + my $hdr_udp = Net::Frame::Layer::UDP->new( + src => $udp->{'src'}, + dst => $udp->{'dst'}, + length => $sz + UDP_HDR_LEN + )->pack; + + my $pkt = pack( "H*", "de" x $sz ); + $pkt = $hdr_ether . $hdr_ipv6 . $hdr_ipv4 . $hdr_udp . $pkt; + + my $pkt_size = length( $pkt ); + + my $hdr = { + tv_sec => 0, + tv_usec => 0, + len => $pkt_size, + caplen => $pkt_size + }; + + return ( $hdr, $pkt ); +} + +# Generate packet originating from the internet +sub gen_inet_packet { + my ( $sz, $ether, $ipv4, $udp ) = @_; + + my $hdr_ether = Net::Frame::Layer::ETH->new( + src => $ether->{'src'}, + dst => $ether->{'dst'}, + type => NF_ETH_TYPE_IPv4 + )->pack; + + my $hdr_ipv4 = Net::Frame::Layer::IPv4->new( + length => $sz + UDP_HDR_LEN + NF_IPv4_HDR_LEN, + ttl => IPv4_TIME_TO_LIVE, + protocol => NF_IPv4_PROTOCOL_UDP, + src => $ipv4->{'src'}, + dst => $ipv4->{'dst'} + )->pack; + + my $hdr_udp = Net::Frame::Layer::UDP->new( + src => $udp->{'src'}, + dst => $udp->{'dst'}, + length => $sz + UDP_HDR_LEN + )->pack; + + my $pkt = pack( "H*", "de" x $sz ); + $pkt = $hdr_ether . $hdr_ipv4 . $hdr_udp . $pkt; + + my $pkt_size = length( $pkt ); + + my $hdr = { + tv_sec => 0, + tv_usec => 0, + len => $pkt_size, + caplen => $pkt_size + }; + + return ( $hdr, $pkt ); +} + +# Read bindings file +sub read_bindings { + my ( $file ) = @_; + + print "Reading bindings file...\n"; + + my @rows; + + open my $fh, "<:encoding(utf8)", $file or die $file . ": $!"; +LINE: while ( my $line = <$fh> ) { + next if ($line =~ /^--.*/); # Skip comments + + my ($ip6, $mac, $ip4, $port); + if ($line =~ /\s*\{.*\},\s*$/) { # Weak check for a data line... + + $line =~ /ip6\s*=\s*ip6\("([^\)]*)"\)/ && do { $ip6 = trim($1); }; + unless ( inet_pton( AF_INET6, $ip6 ) ) { print "ERROR - Invalid ipv6: $ip6\n"; next LINE; } + + $line =~ /ip\s*=\s*ip\("([^\)]*)"\)/ && do { $ip4 = trim($1); }; + unless ( inet_pton( AF_INET, $ip4 ) ) { print "ERROR - Invalid ipv4: $ip4\n"; next LINE; } + + $line =~ /mac\s*=\s*mac\("([^\)]*)"\)/ && do { $mac = trim($1); }; + unless ( $mac =~ /^([0-9a-f]{2}([:-]|$)){6}$/i ) { print "ERROR - Invalid mac: $mac\n"; next LINE; } + + $line =~ /port\s*=\s*([0-9]*)/ && do { $port = trim($1); }; + unless ( int($port) ) { print "ERROR - Invalid port number: $port\n"; next LINE; } + + push @rows, { + ipv6 => $ip6, + mac => $mac, + ipv4 => $ip4, + port => $port + } + } + } + close $fh; + + return @rows; +} + +# Generate packets originating from CPE +sub gen_tun_pcap { + my ( $binding_file, $pkt_count ) = @_; + my @bind = read_bindings($binding_file); + my $idx = 0; + my $row; + my $public_port = 0; + + print "Generating $pkt_count Tunnel packets...\n"; + + my $max = @bind; + for( my $i=0; $i<$pkt_count; $i++ ) { + + $idx = rand $max; + $row = @bind[$idx]; + + $public_port = rand_port( $row->{port}, 0x3f ); + + my ( $hdr, $pkt ) = gen_tun_packet( + $size, + { src => $row->{mac}, dst => ETHER_STATIC_MAC }, + { src => $row->{ipv6}, dst => IPv6_STATIC_IP }, + { src => $row->{ipv4}, dst => IPv4_STATIC_IP }, + { src => $public_port, dst => UDP_STATIC_PORT } + ); + + pcap_dump( $dumper, $hdr, $pkt ); + } +} + +# Generate packets originating from the internet +sub gen_inet_pcap { + my ( $binding_file, $pkt_count ) = @_; + my @bind = read_bindings($binding_file); + my $idx = 0; + my $row; + my $public_port = 0; + + print "Generating $pkt_count Internet packets...\n"; + + my $max = @bind; + for( my $i=0; $i<$pkt_count; $i++ ) { + + $idx = rand $max; + $row = @bind[$idx]; + + $public_port = rand_port( $row->{port}, 0x3f ); + + my ( $hdr, $pkt ) = gen_inet_packet( + $size, + { src => ETHER_STATIC_MAC, dst => $row->{mac} }, + { src => IPv4_STATIC_IP, dst => $row->{ipv4} }, + { src => UDP_STATIC_PORT, dst => $public_port } + ); + + pcap_dump( $dumper, $hdr, $pkt ); + } +} diff --git a/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/ipv6_tun_bindings.pl b/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/ipv6_tun_bindings.pl new file mode 100755 index 00000000..02af5103 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/ipv6_tun/ipv6_tun_bindings.pl @@ -0,0 +1,266 @@ +#!/usr/bin/perl + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +=head1 NAME + +ipv6_tun_bindings.pl + +=head1 SYNOPSIS + + ipv6_tun_bindings.pl [-n <num_entries>] [-tun_ip <ipv6>] [-mac <next_hop_mac>] + [-pub_ip <ipv4>] [-port <begin>-<end>] [-set <num_ports>] + [-suffix <suffix>] [-test <num_entries>] [-sym|-nosym] + [-help] + +=head1 DESCRIPTION + +This script can be used to generate a binding table for the IPv6 Tunnel +task implemented in PROX (ipv6_encap and ipv6_decap). +The entries in this table bind a specific tunnel endpoint (lwB4 in lw4over6 +architecture) to a public IPv4 address and port set. +The port set is actually derived from the port specified in the table +and a port bitmask in the PROX task configuration ("lookup port mask"). + +The ipv6_encap task uses the binding table to know where to tunnel IPv4 +traffic to. The ipv6_decap task uses the table to verify tunnel packets +have a valid public IPv4 and port combination for the originating tunnel. + +The table uses the Lua syntax so it can be loaded into PROX. Example: +return { + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0000"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4608}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0001"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4672}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0002"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4736}, + {ip6 = ip6("fe80:0000:0000:0000:0200:00ff:fe00:0003"), mac = mac("fe:80:00:00:00:00"), ip = ip("171.205.239.1"), port = 4800}, +} + +The script generates consecutive entries, starting from a given IP address +and assigning ports within a given range, increasing the port number by a +fixed amount which should correspond to the port lookup mask being used. + +UDF table: In addition to the binding table itself, the script can optionally +generate accompanying UDF tables for generating test traffic matching the +binding table. Such UDF tables can then be used in a traffic generation tool. + +=head1 OPTIONS + +=over 22 + +=item -n <num_entries> + +How many entries in the binding table + +=item -tun_ip <ipv6> + +Starting tunnel endpoint IPv6 address (will be incremented) + +=item -mac <next_hop_mac> + +MAC address of the next hop to reach the tunnel endpoints + +=item -pub_ip <ipv4> + +Starting public IPv4 address + +=item -port <begin>-<end> + +Range of ports where to assign Port Sets + +=item -set <num_ports> + +Number of ports in set (should be a power of 2 because bitmasking is used +in lwAFTR) + +=item -suffix <suffix> + +Filename suffix to use for the generated file(s) + +=item -test <num_entries> + +Number of random entries to put into test UDF table + +=item -sym + +Whether the same random entry from the table should be inserted into both +traffic sides or if different entries should be used + +=item -help + +Shows the full script documentation. + +=back + +=head1 AUTHOR + + Copyright(c) 2010-2017 Intel Corporation. + All rights reserved. + +=cut + + +use strict vars; +use Getopt::Long; +use Pod::Usage; +use Socket qw(AF_INET AF_INET6 inet_ntop inet_pton); + +sub parse_ip +{ + my ($str, $ip_ref, $family) = @_; + + my $packed = inet_pton($family, $str); + return 0 if (!defined($packed)); + + if ($family == AF_INET6) { + #print unpack("H*", $packed). "\n"; + my @w = unpack("NNNN", $packed); + my ($high, $low) = (($w[0] << 32) | $w[1], ($w[2] << 32) | $w[3]); + @$ip_ref = ($high, $low); + } + else { + $$ip_ref = unpack("N", $packed); + } + return 1; +} + +sub ntop6 +{ + my ($in) = @_; + my $packed = pack('NNNN', $in->[0] >> 32, $in->[0] & 0xffffffff, + $in->[1] >> 32, $in->[1] & 0xffffffff); + return inet_ntop(AF_INET6, $packed); +} + +sub ntop6_expanded +{ + my ($in) = @_; + return sprintf('%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x', + ($in->[0] >> 48) & 0xffff, ($in->[0] >> 32) & 0xffff, + ($in->[0] >> 16) & 0xffff, ($in->[0] ) & 0xffff, + ($in->[1] >> 48) & 0xffff, ($in->[1] >> 32) & 0xffff, + ($in->[1] >> 16) & 0xffff, ($in->[1] ) & 0xffff); +} + +my ($tun_ip_str, $pub_ip_str, $ports_str); + +GetOptions( + 'help' => sub () { Pod::Usage::pod2usage( -verbose => 2 ); exit; }, + 'n=i' => \(my $num_B4s = 10), + 'tun_ip=s' => \(my $tun_ip_str = 'fe80:0000:0000:0000:0200:00ff:0000:0000'), + 'pub_ip=s' => \(my $pub_ip_str = '171.205.239.1'), + 'mac=s' => \(my $next_hop_mac = 'fe:80:00:00:00:00'), + 'port=s' => \(my $ports_str='4608-11968'), + 'set=n' => \(my $port_set_sz = 64), + 'suffix=s' => \(my $suffix = ''), + 'test=n' => \(my $num_test_lines = 200000), + 'sym!' => \(my $symmetric_traffic = TRUE), +) or pod2usage(-verbose => 1) && exit; + +my @tun_ip; +parse_ip($tun_ip_str, \@tun_ip, AF_INET6) or print("Invalid starting tunnel IP: $tun_ip_str\n") && pod2usage(-verbose => 1) && exit; +parse_ip($pub_ip_str, \(my $pub_ip), AF_INET) or print("Invalid starting public IP: $pub_ip_str\n") && pod2usage(-verbose => 1) && exit; +my @port_range; +if ($ports_str =~ /^([^d]+)\s*\-\s*([^d]+)$/) { + @port_range = ($1, $2); +} +else { print "Invalid port range: $ports_str\n"; pod2usage(-verbose => 1); exit } + +# Summary of input data +print "File suffix: $suffix\n" if ($suffix); +print "Starting Tunnel IP: " . ntop6(\@tun_ip) . "\n"; +print "Starting Public IP: ".inet_ntop(AF_INET, pack("N", $pub_ip)) . "\n"; +print "Public Port Range: $port_range[0]-$port_range[1] by blocks of $port_set_sz\n"; + +my @data; # Holds generated binding table, so we can later generate test traffic for it + +# Binding table for PROX IPv6 Tunnel +my $filename = 'ip6_tun_bind'.$suffix.'.lua'; +print "\nGenerating binding table with $num_B4s entries into $filename ... "; +open(my $fh, '>', $filename) or die "Could not open file '$filename' $!"; +print $fh "-- Bindings for lwaftr: lwB4 IPv6 address, next hop MAC address\n"; +print $fh "-- towards lwB4, IPv4 Public address, IPv4 Public Port Set\n"; +print $fh "\n"; +print $fh "return {" . "\n"; +my $port = $port_range[0]; +for (my $B4_id = 0; $B4_id < $num_B4s; $B4_id++) { + $data[$B4_id]{'b4_ipv6'} = ntop6_expanded(\@tun_ip); + $data[$B4_id]{'pub_ipv4'} = "" . (($pub_ip >> 24) & 0xff) . "." . (($pub_ip >> 16) & 0xff) . "." . (($pub_ip >> 8) & 0xff) . "." . ($pub_ip & 0xff); + $data[$B4_id]{'pub_port'} = $port; + $data[$B4_id]{'next_hop_mac'} = $next_hop_mac; + + print $fh " {"; + print $fh "ip6 = ip6(\"" . $data[$B4_id]{'b4_ipv6'} . "\")"; + print $fh ", mac = mac(\"" . $data[$B4_id]{'next_hop_mac'} . "\")"; + print $fh ", ip = ip(\"" . $data[$B4_id]{'pub_ipv4'} . "\")"; + print $fh ", port = " . $data[$B4_id]{'pub_port'}; + print $fh "},\n"; + + $port += $port_set_sz; + if ($port > $port_range[1]) { + $pub_ip++; + $port = $port_range[0]; + } + + # Move to next Tunnel address + if (@tun_ip[1] < 0xffffffffffffffff) { + @tun_ip[1]++; + } else { + @tun_ip[0]++; + @tun_ip[1] = 0; + } +} +print $fh "}" . "\n"; +close $fh; +print "[DONE]\n"; + +# Test traffic "UDF Tables" +if ($num_test_lines) { + print "Generating $num_test_lines lines of test UDF table into lwAFTR_tun|inet".$suffix.".csv ... "; + + # Tunnel Packets from B4 to lwAFTR + my $filename = 'lwAFTR_tun' . $suffix . '.csv'; + open(my $fh_tun, '>', $filename) or die "Could not open file '$filename' $!"; + print $fh_tun "b4_ip,pub_ip,pub_port\n"; + print $fh_tun "22,66,74\n"; # Offsets + print $fh_tun "16,4,2\n"; # Sizes + print $fh_tun "6,5,3\n"; # Format (IPv6, IPv4, Decimal) + print $fh_tun ",,\n"; + + # Internet Packets towards the lwAFTR, to be sent to corresp lwB4 over tunnel + my $filename = 'lwAFTR_inet' . $suffix . '.csv'; + open(my $fh_inet, '>', $filename) or die "Could not open file '$filename' $!"; + print $fh_inet "pub_ip,pub_port\n"; + print $fh_inet "30,36\n"; # Offsets + print $fh_inet "4,2\n"; # Sizes + print $fh_inet "5,3\n"; # Format (IPv6, IPv4, Decimal) + print $fh_inet ",,\n"; + + for (my $i = 0; $i < $num_test_lines; $i++) { + my $B4_id = int(rand($num_B4s)); + my $port = $data[$B4_id]{'pub_port'} + int(rand($port_set_sz)); + printf $fh_tun $data[$B4_id]{'b4_ipv6'} . "," . $data[$B4_id]{'pub_ipv4'} . "," . $port . "\n"; + + if (! $symmetric_traffic) { + $B4_id = int(rand($num_B4s)); + $port = $data[$B4_id]{'pub_port'} + int(rand($port_set_sz)); + } + printf $fh_inet $data[$B4_id]{'pub_ipv4'} . "," . $port . "\n"; + } + + close $fh_tun; + close $fh_inet; + print "[DONE]\n"; +} diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README new file mode 100644 index 00000000..49d819d8 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README @@ -0,0 +1,57 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +rapid (Rapid Automated Performance Indication for Dataplane) +************************************************************ + +rapid is a set of files offering an easy way to do a sanity check of the +dataplane performance of an OpenStack environment. + +Copy the files in a directory on a machine that can run the OpenStack CLI +commands and that can reach the OpenStack public network. Also create a qcow2 +image in the same directory with the following characteristics: +* Name of the qcow2 file should be: rapidVM.qcow2 + This default name can be changed on the rapid command line +* Should have DPDK and PROX installed. PROX should be in /root/prox/ directory +* Image should have cloud-init installed + +Source the openrc file of the OpenStack environment so that the OpenStack CLI +commands can be run: + # source openrc +Now you can run the rapid.py file. Use help for more info on the usage: + # ./rapid.py --help + +rapid will use the OpenStack CLI to create the flavor, key-pair, network, image, +stack, ... +Then it will connect to the 2 VMs that have been instantiated and it will launch +PROX in both VMs. +Once that is done it will connect to the PROX tcp socket and start sending +commands to run the actual test. +It will print test results on the screen while running. +The PROX instance in the Generator VM will generate packets which will arrive in +the PROX instance running on the SUT (System Under Test) VM. The SUT will then +send the packets back to the generator by swapping source and destination. + +Notes about prox_gen_user_data.sh and prox_sut_user_data.sh scripts: +- These scripts contain commands that will be executed using cloud-init at + startup of the VMs. They contain a hard-coded PCI address for the DPDK + interface that will be used by PROX. You might want to check that this is + actually the right PCI address. +- These scripts also assume some specific DPDK directory and tools which might + change over different DPDK release. They have been tested with DPDK-17.02. +- These scripts are also assuming that this interface is on the "dpdk-network" + network managed by OpenStack. + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg new file mode 100644 index 00000000..522eb801 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg @@ -0,0 +1,64 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[lua] +dofile("parameters.lua") + +[port 0] +name=p0 + +[variables] +$mbs=8 + +[defaults] +mempool size=4K + +[global] +name=Basic Gen + +[core 0] +mode=master + +[core 1] +name=p0 +task=0 +mode=gen +sub mode=l3 +rx ring=yes +tx port=p0 +bps=1250000000 +pkt inline=00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d ${gen_hex_ip} ${sut_hex_ip} 0b b8 0b b9 00 08 55 7b +gateway ipv4=${sut_ip} +local ipv4=${gen_ip} +min bulk size=$mbs +;random=XXXXXXXXXXXXXXXX +;random=0000000000XXXXXX ; 64 possibilities +;rand_offset=34 ; SOURCE UDP PORT +;random=XXXXXXXXXXXXXXXX +;random=000000000XXXXXXX ; 128 +;rand_offset=36 ; DESTINTAITON UDP PORT + +[core 2] +task=0 +mode=arp +rx port=p0,p0,p0,p0 +tx port=p0 +tx cores=1t0 + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py new file mode 100644 index 00000000..b384e9f0 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py @@ -0,0 +1,218 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from __future__ import print_function + +import os +import subprocess +import socket + +class prox_ctrl(object): + def __init__(self, ip, key=None, user=None): + self._ip = ip + self._key = key + self._user = user + self._children = [] + self._proxsock = [] + + def ip(self): + return self._ip + + def connect(self): + """Simply try to run 'true' over ssh on remote system. + On failure, raise RuntimeWarning exception when possibly worth + retrying, and raise RuntimeError exception otherwise. + """ + return self.run_cmd('true', True) + + def close(self): + """Must be called before program termination.""" + for prox in self._proxsock: + prox.quit() + children = len(self._children) + if children == 0: + return + if children > 1: + print('Waiting for %d child processes to complete ...' % children) + for child in self._children: + ret = os.waitpid(child[0], os.WNOHANG) + if ret[0] == 0: + print("Waiting for child process '%s' to complete ..." % child[1]) + ret = os.waitpid(child[0], 0) + rc = ret[1] + if os.WIFEXITED(rc): + if os.WEXITSTATUS(rc) == 0: + print("Child process '%s' completed successfully" % child[1]) + else: + print("Child process '%s' returned exit status %d" % ( + child[1], os.WEXITSTATUS(rc))) + elif os.WIFSIGNALED(rc): + print("Child process '%s' exited on signal %d" % ( + child[1], os.WTERMSIG(rc))) + else: + print("Wait status for child process '%s' is 0x%04x" % ( + child[1], rc)) + + def run_cmd(self, command, _connect=False): + """Execute command over ssh on remote system. + Wait for remote command completion. + Return command output (combined stdout and stderr). + _connect argument is reserved for connect() method. + """ + cmd = self._build_ssh(command) + try: + return subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + if _connect and ex.returncode == 255: + raise RuntimeWarning(ex.output.strip()) + raise RuntimeError('ssh returned exit status %d:\n%s' + % (ex.returncode, ex.output.strip())) + + def fork_cmd(self, command, name=None): + """Execute command over ssh on remote system, in a child process. + Do not wait for remote command completion. + Return child process id. + """ + if name is None: + name = command + cmd = self._build_ssh(command) + pid = os.fork() + if (pid != 0): + # In the parent process + self._children.append((pid, name)) + return pid + # In the child process: use os._exit to terminate + try: + # Actually ignore output on success, but capture stderr on failure + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + raise RuntimeError("Child process '%s' failed:\n" + 'ssh returned exit status %d:\n%s' + % (name, ex.returncode, ex.output.strip())) + os._exit(0) + + def prox_sock(self, port=8474): + """Connect to the PROX instance on remote system. + Return a prox_sock object on success, None on failure. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((self._ip, port)) + prox = prox_sock(sock) + self._proxsock.append(prox) + return prox + except: + return None + + def scp_put(self, src, dst): + """Copy src file from local system to dst on remote system.""" + cmd = [ 'scp', + '-B', + '-oStrictHostKeyChecking=no', + '-oUserKnownHostsFile=/dev/null', + '-oLogLevel=ERROR' ] + if self._key is not None: + cmd.extend(['-i', self._key]) + cmd.append(src) + remote = '' + if self._user is not None: + remote += self._user + '@' + remote += self._ip + ':' + dst + cmd.append(remote) + try: + # Actually ignore output on success, but capture stderr on failure + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + raise RuntimeError('scp returned exit status %d:\n%s' + % (ex.returncode, ex.output.strip())) + + def _build_ssh(self, command): + cmd = [ 'ssh', + '-oBatchMode=yes', + '-oStrictHostKeyChecking=no', + '-oUserKnownHostsFile=/dev/null', + '-oLogLevel=ERROR' ] + if self._key is not None: + cmd.extend(['-i', self._key]) + remote = '' + if self._user is not None: + remote += self._user + '@' + remote += self._ip + cmd.append(remote) + cmd.append(command) + return cmd + +class prox_sock(object): + def __init__(self, sock): + self._sock = sock + self._rcvd = b'' + + def quit(self): + if self._sock is not None: + self._send('quit') + self._sock.close() + self._sock = None + + def start(self, cores): + self._send('start %s' % ','.join(map(str, cores))) + + def stop(self, cores): + self._send('stop %s' % ','.join(map(str, cores))) + + def speed(self, speed, cores, tasks=None): + if tasks is None: + tasks = [ 0 ] * len(cores) + elif len(tasks) != len(cores): + raise ValueError('cores and tasks must have the same len') + for (core, task) in zip(cores, tasks): + self._send('speed %s %s %s' % (core, task, speed)) + + def reset_stats(self): + self._send('reset stats') + + def core_stats(self, cores, task=0): + rx = tx = drop = tsc = hz = 0 + self._send('core stats %s %s' % (','.join(map(str, cores)), task)) + for core in cores: + stats = self._recv().split(',') + rx += int(stats[0]) + tx += int(stats[1]) + drop += int(stats[2]) + tsc = int(stats[3]) + hz = int(stats[4]) + return rx, tx, drop, tsc, hz + + def set_random(self, cores, task, offset, mask, length): + self._send('set random %s %s %s %s %s' % (','.join(map(str, cores)), task, offset, mask, length)) + + def _send(self, cmd): + """Append LF and send command to the PROX instance.""" + if self._sock is None: + raise RuntimeError("PROX socket closed, cannot send '%s'" % cmd) + self._sock.sendall(cmd.encode() + b'\n') + + def _recv(self): + """Receive response from PROX instance, and return it with LF removed.""" + if self._sock is None: + raise RuntimeError("PROX socket closed, cannot receive anymore") + pos = self._rcvd.find(b'\n') + while pos == -1: + self._rcvd += self._sock.recv(256) + pos = self._rcvd.find(b'\n') + rsp = self._rcvd[:pos] + self._rcvd = self._rcvd[pos+1:] + return rsp.decode() + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh new file mode 100644 index 00000000..e7f58a9f --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +echo 128 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages +mount -t hugetlbfs nodev /mnt/huge +modprobe uio +insmod /root/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko +/root/dpdk/usertools/dpdk-devbind.py --force --bind igb_uio 00:04.0 +iptables -F diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh new file mode 100644 index 00000000..e7f58a9f --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +echo 128 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages +mount -t hugetlbfs nodev /mnt/huge +modprobe uio +insmod /root/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko +/root/dpdk/usertools/dpdk-devbind.py --force --bind igb_uio 00:04.0 +iptables -F diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py new file mode 100755 index 00000000..1a0ea41c --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py @@ -0,0 +1,445 @@ +#!/usr/bin/python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import sys +import time +import subprocess +import getopt +from prox_ctrl import prox_ctrl + +version="17.04.19" +stack = "rapidTestEnv" #Default string for stack +yaml = "rapid.yaml" #Default string for yaml file +key = "prox" # This is also the default in the yaml file.... +flavor = "prox_flavor" # This is also the default in the yaml file.... +image = "rapidVM" # This is also the default in the yaml file.... +image_file = "rapidVM.qcow2" +network = "dpdk-network" # This is also the default in the yaml file.... +subnet = "dpdk-subnet" #Hardcoded at this moment + +def usage(): + print("usage: rapid [--version] [-v]") + print(" [--stack STACK_NAME]") + print(" [--yaml YAML_FILE]") + print(" [--key KEY_NAME]") + print(" [--flavor FLAVOR_NAME]") + print(" [--image IMAGE_NAME]") + print(" [--image_file IMAGE_FILE]") + print(" [--network NETWORK]") + print(" [-h] [--help]") + print("") + print("Command-line interface to RAPID") + print("") + print("optional arguments:") + print(" -v, --version Show program's version number and exit") + print(" --stack STACK_NAME Specify a name for the heat stack. Default is rapidTestEnv.") + print(" --yaml YAML_FILE Specify the yaml file to be used. Default is rapid.yaml.") + print(" --key KEY_NAME Specify the key to be used. Default is prox.") + print(" --flavor FLAVOR_NAME Specify the flavor to be used. Default is prox_flavor.") + print(" --image IMAGE_NAME Specify the image to be used. Default is rapidVM.") + print(" --image_file IMAGE_FILE Specify the image qcow2 file to be used. Default is rapidVM.qcow2.") + print(" --network NETWORK Specify the network name to be used for the dataplane. Default is dpdk-network.") + print(" -h, --help Show help message and exit.") + print("") + print("To delete the rapid stack, type the following command") + print(" openstack stack delete --yes --wait DPTestEnv") + print("Note that rapidTestEnv is the default stack name. Replace with STACK_NAME if needed") + +try: + opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "yaml=","stack=","key=","flavor=","image=","network="]) +except getopt.GetoptError as err: + print("===========================================") + print str(err) + print("===========================================") + usage() + sys.exit(2) +if args: + usage() + sys.exit(2) +for opt, arg in opts: + if opt in ("-h", "--help"): + usage() + sys.exit() + if opt in ("-v", "--version"): + print("Rapid Automated Performance Indication for Dataplane "+version) + sys.exit() + if opt in ("--stack"): + stack = arg + print ("Using '"+stack+"' as name for the stack") + elif opt in ("--yaml"): + yaml = arg + print ("Using stack: "+yaml) + elif opt in ("--key"): + key = arg + print ("Using key: "+key) + elif opt in ("--flavor"): + flavor = arg + print ("Using flavor: "+flavor) + elif opt in ("--image"): + image = arg + print ("Using image: "+image) + elif opt in ("--image_file"): + image_file = arg + print ("Using qcow2 file: "+image_file) + elif opt in ("--network"): + network = arg + print ("Using network: "+ network) + +print("Checking image: "+image) +cmd = 'openstack image show '+image+' |grep "status " | tr -s " " | cut -d" " -f 4' +ImageExist = subprocess.check_output(cmd , shell=True).strip() +if ImageExist == 'active': + print("Image already available") +else: + print('Creating image ...') + cmd = 'openstack image create --disk-format qcow2 --container-format bare --public --file ./'+image_file+ ' ' +image+' |grep "status " | tr -s " " | cut -d" " -f 4' + ImageExist = subprocess.check_output(cmd , shell=True).strip() + if ImageExist == 'active': + print('Image created and active') + cmd = 'openstack image set --property hw_vif_multiqueue_enabled="true" ' +image + subprocess.check_call(cmd , shell=True) + else : + raise Exception("Failed to create image") + +print("Checking key: "+key) +cmd = 'openstack keypair show '+key+' |grep "name " | tr -s " " | cut -d" " -f 4' +KeyExist = subprocess.check_output(cmd , shell=True).strip() +if KeyExist == key: + print("Key already installed") +else: + print('Creating key ...') + cmd = 'openstack keypair create '+ key + '>' +key+'.pem' + subprocess.check_call(cmd , shell=True) + cmd = 'chmod 600 ' +key+'.pem' + subprocess.check_call(cmd , shell=True) + cmd = 'openstack keypair show '+key+' |grep "name " | tr -s " " | cut -d" " -f 4' + KeyExist = subprocess.check_output(cmd , shell=True).strip() + if KeyExist == key: + print("Key created") + else : + raise Exception("Failed to create key: " + key) + +print("Checking flavor: "+flavor) +cmd = 'openstack flavor show '+flavor+' |grep "name " | tr -s " " | cut -d" " -f 4' +FlavorExist = subprocess.check_output(cmd , shell=True).strip() +if FlavorExist == flavor: + print("Flavor already installed") +else: + print('Creating flavor ...') + cmd = 'openstack flavor create '+flavor+' --ram 8192 --disk 80 --vcpus 4 |grep "name " | tr -s " " | cut -d" " -f 4' + FlavorExist = subprocess.check_output(cmd , shell=True).strip() + if FlavorExist == flavor: + cmd = 'openstack flavor set '+ flavor +' --property hw:mem_page_size="large" --property hw:cpu_policy="dedicated" --property hw:cpu_threads_policy="isolate"' + subprocess.check_call(cmd , shell=True) + print("Flavor created") + else : + raise Exception("Failed to create flavor: " + flavor) + +print("Checking network: "+network) +cmd = 'openstack network show '+network+' |grep "status " | tr -s " " | cut -d" " -f 4' +NetworkExist = subprocess.check_output(cmd , shell=True).strip() +if NetworkExist == 'ACTIVE': + print("Network already active") +else: + print('Creating network ...') + cmd = 'openstack network create '+network+' |grep "status " | tr -s " " | cut -d" " -f 4' + NetworkExist = subprocess.check_output(cmd , shell=True).strip() + if NetworkExist == 'ACTIVE': + print("Network created") + else : + raise Exception("Failed to create network: " + network) + +print("Checking subnet: "+subnet) +cmd = 'neutron subnet-show '+ subnet+' |grep "name " | tr -s " " | cut -d" " -f 4' +SubnetExist = subprocess.check_output(cmd , shell=True).strip() +if SubnetExist == subnet: + print("Subnet already exists") +else: + print('Creating subnet ...') + cmd = 'neutron subnet-create --name '+ subnet+ ' ' +network+' 10.10.10.0/24 |grep "name " | tr -s " " | cut -d" " -f 4' + SubnetExist = subprocess.check_output(cmd , shell=True).strip() + if SubnetExist == subnet: + print("Subnet created") + else : + raise Exception("Failed to create subnet: " + subnet) + +print("Checking Stack: "+stack) +cmd = 'openstack stack show '+stack+' |grep "stack_status " | tr -s " " | cut -d" " -f 4' +StackRunning = subprocess.check_output(cmd , shell=True).strip() +if StackRunning == '': + print('Creating Stack ...') + cmd = 'openstack stack create -t '+ yaml + ' --parameter flavor="'+flavor +'" --parameter key="'+ key + '" --parameter image="'+image + '" --parameter dpdk_network="'+network+'" --wait '+stack +' |grep "stack_status " | tr -s " " | cut -d" " -f 4' + StackRunning = subprocess.check_output(cmd , shell=True).strip() +if StackRunning != 'CREATE_COMPLETE': + raise Exception("Failed to create stack") + +print('Stack running') +genName=stack+'-gen' +sutName=stack+'-sut' +cmd = 'nova list | grep '+ genName +' | tr -s " " | cut -d " " -f 4' +genVMName = subprocess.check_output(cmd , shell=True).strip() +print('Generator: '+ genVMName) +cmd = 'nova list | grep '+ sutName +' | tr -s " " | cut -d " " -f 4' +sutVMName = subprocess.check_output(cmd , shell=True).strip() +print('SUT: '+ sutVMName) +cmd='nova show ' + genVMName + ' | grep "dpdk-network" | tr -s " " | cut -d" " -f 5' +genDPIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + genVMName + ' | grep "admin_internal_net" | tr -s " " | cut -d" " -f 6' +genAdminIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + sutVMName + ' | grep "dpdk-network" | tr -s " " | cut -d" " -f 5' +sutDPIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + sutVMName + ' | grep "admin_internal_net" | tr -s " " | cut -d" " -f 6' +sutAdminIP = subprocess.check_output(cmd , shell=True).strip() + +#======================================================================== +def connect_socket(client): + attempts = 1 + print("Trying to connect to PROX (just launched) on %s, attempt: %d" + % (client.ip(), attempts)) + sock = None + while True: + sock = client.prox_sock() + if sock is not None: + break + attempts += 1 + if attempts > 20: + raise Exception("Failed to connect to PROX on %s after %d attempts" + % (client.ip(), attempts)) + time.sleep(10) + print("Trying to connect to PROX (just launched) on %s, attempt: %d" + % (client.ip(), attempts)) + print("Connected to PROX on %s" % client.ip()) + return sock + +def connect_client(client): + attempts = 1 + print ("Trying to connect to VM which was just launched on %s, attempt: %d" + % (client.ip(), attempts)) + while True: + try: + client.connect() + break + except RuntimeWarning, ex: + attempts += 1 + if attempts > 20: + raise Exception("Failed to connect to VM after %d attempts:\n%s" + % (attempts, ex)) + time.sleep(15) + print ("Trying to connect to VM which was just launched on %s, attempt: %d" + % (client.ip(), attempts)) + print("Connected to VM on %s" % client.ip()) + + +def run_testA(): + global genclient + global sutclient + ip = genDPIP.split('.') + hexgenDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2) + ip = sutDPIP.split('.') + hexsutDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2) + with open("parameters.lua", "w") as f: + f.write('gen_hex_ip="'+hexgenDPIP+'"\n') + f.write('sut_hex_ip="'+hexsutDPIP+'"\n') + f.write('gen_ip="'+genDPIP+'"\n') + f.write('sut_ip="'+sutDPIP+'"\n') + f.close + genclient.scp_put('./gen.cfg', '/root/gen.cfg') + sutclient.scp_put('./sut.cfg', '/root/sut.cfg') + genclient.scp_put('./parameters.lua', '/root/parameters.lua') + sutclient.scp_put('./parameters.lua', '/root/parameters.lua') + print("Config files copied") + cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg' + genclient.fork_cmd(cmd, 'PROX GEN') + cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg' + sutclient.fork_cmd(cmd, 'PROX SUT') + gensock = connect_socket(genclient) + sutsock = connect_socket(sutclient) + new_speed = 100 + attempts = 0 + cores = [1,2] + gencores = [1] + gensock.reset_stats() + sutsock.reset_stats() + gensock.start([2]) + print("+---------------------------------------------------------------------------------------------------------+") + print("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT. SUT sends packets back |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + print("| Test | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen | Result |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + while (new_speed > 0.1): + attempts += 1 + # Start generating packets at requested speed (in % of a 10Gb/s link) + gensock.speed(new_speed, gencores) + gensock.start(gencores) + time.sleep(1) + # Get statistics now that the generation is stable and NO ARP messages any more + old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats([1]) + old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(cores) + time.sleep(10) + # Get statistics after some execution time + new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(cores) + new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats([1]) + time.sleep(1) + # Stop generating + gensock.stop(gencores) + drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM + rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM + tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface + tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles. + sut_rx = new_sut_rx - old_sut_rx + sut_tx = new_sut_tx - old_sut_tx + sut_tsc = new_sut_tsc - old_sut_tsc + if (tx == 0): + raise Exception("TX = 0") + drop_rate = round(((drop-rx) * 100.0)/(tx+drop-rx),1) + pps_req_tx = round((tx+drop-rx)*tsc_hz*1.0/(tsc*1000000),5) + pps_tx = round(tx*tsc_hz*1.0/(tsc*1000000),5) + pps_rx = round(rx*tsc_hz*1.0/(tsc*1000000),5) + pps_sut_tx = round(sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000),5) + if ((drop_rate) < 1): + # This will stop the test when number of dropped packets is below a certain percentage + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + print('|{:>5}'.format(str(attempts))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | SUCCESS |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + break + else: + print('|{:>5}'.format(str(attempts))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | FAILED |") + # Following calculates the ratio for the new speed to be applied + # On the Y axis, we will find the ratio, a number between 0 and 1 + # On the x axis, we find the % of dropped packets, a number between 0 and 100 + # 2 lines are drawn and we take the minumun of these lines to calculate the ratio + # One line goes through (0,y0) and (p,q) + # The second line goes through (p,q) and (100,y100) + y0=0.99 + y100=0.1 + p=15 + q=.9 + ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100)) + new_speed = (int(new_speed*ratio*100)+0.5)/100 + gensock.quit() + sutsock.quit() + time.sleep(2) + print("") + +def run_testB(): + global genclient + global sutclient + ip = genDPIP.split('.') + hexgenDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2) + ip = sutDPIP.split('.') + hexsutDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2) + with open("parameters.lua", "w") as f: + f.write('gen_hex_ip="'+hexgenDPIP+'"\n') + f.write('sut_hex_ip="'+hexsutDPIP+'"\n') + f.write('gen_ip="'+genDPIP+'"\n') + f.write('sut_ip="'+sutDPIP+'"\n') + f.close + genclient.scp_put('./gen.cfg', '/root/gen.cfg') + sutclient.scp_put('./sut.cfg', '/root/sut.cfg') + genclient.scp_put('./parameters.lua', '/root/parameters.lua') + sutclient.scp_put('./parameters.lua', '/root/parameters.lua') + print("Config files copied") + cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg' + genclient.fork_cmd(cmd, 'PROX GEN') + cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg' + sutclient.fork_cmd(cmd, 'PROX SUT') + gensock = connect_socket(genclient) + sutsock = connect_socket(sutclient) + print("+----------------------------------------------------------------------------------------------+") + print("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + print("| Flows | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + cores = [1,2] + gencores = [1] + gensock.start([2]) + new_speed = 100 + # To generate a desired number of flows, PROX will randomize the bits in source and destination ports, as specified by the bit masks in the flows variable. + flows={128:['0000000000000XXX','000000000000XXXX'],1024:['00000000000XXXXX','00000000000XXXXX'],8192:['0000000000XXXXXX','000000000XXXXXXX'],65535:['00000000XXXXXXXX','00000000XXXXXXXX'],524280:['0000000XXXXXXXXX','000000XXXXXXXXXX']} + for flow_number in sorted(flows.iterkeys()): + #new_speed = 100 Commented out: Not starting from 100% since we are trying more flows, so speed will not be higher than the speed achieved in previous loop + attempts = 0 + gensock.reset_stats() + sutsock.reset_stats() + source_port,destination_port = flows[flow_number] + gensock.set_random(gencores,0,34,source_port,2) + gensock.set_random(gencores,0,36,destination_port,2) + while (new_speed > 0.1): + attempts += 1 + # Start generating packets at requested speed (in % of a 10Gb/s link) + gensock.speed(new_speed, gencores) + gensock.start(gencores) + time.sleep(1) + # Get statistics now that the generation is stable and NO ARP messages any more + old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats([1]) + old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(cores) + time.sleep(10) + # Get statistics after some execution time + new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(cores) + new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats([1]) + time.sleep(1) + # Stop generating + gensock.stop(gencores) + drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM + rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM + tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface + tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles. + sut_rx = new_sut_rx - old_sut_rx + sut_tx = new_sut_tx - old_sut_tx + sut_tsc = new_sut_tsc - old_sut_tsc + if (tx == 0): + raise Exception("TX = 0") + drop_rate = round(((drop-rx) * 100.0)/(tx+drop-rx),1) + pps_req_tx = round((tx+drop-rx)*tsc_hz*1.0/(tsc*1000000),5) + pps_tx = round(tx*tsc_hz*1.0/(tsc*1000000),5) + pps_rx = round(rx*tsc_hz*1.0/(tsc*1000000),5) + pps_sut_tx = round(sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000),5) + if ((drop_rate) < 1): + # This will stop the test when number of dropped packets is below a certain percentage + print('|{:>7}'.format(str(flow_number))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + break + # Following calculates the ratio for the new speed to be applied + # On the Y axis, we will find the ratio, a number between 0 and 1 + # On the x axis, we find the % of dropped packets, a number between 0 and 100 + # 2 lines are drawn and we take the minumun of these lines to calculate the ratio + # One line goes through (0,y0) and (p,q) + # The second line goes through (p,q) and (100,y100) + y0=0.99 + y100=0.1 + p=15 + q=.9 + ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100)) + new_speed = (int(new_speed*ratio*100)+0.5)/100 + gensock.quit() + sutsock.quit() + time.sleep(2) + print("") + +#======================================================================== +genclient = prox_ctrl(genAdminIP, key+'.pem') +connect_client(genclient) +sutclient = prox_ctrl(sutAdminIP, key+'.pem') +connect_client(sutclient) +##################################################################################### +run_testA() +run_testB() +##################################################################################### +genclient.close() +sutclient.close() + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml new file mode 100644 index 00000000..eab957f5 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml @@ -0,0 +1,105 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +heat_template_version: 2016-04-08 +description: RAPID stack (Rapid Automated Performance Indication for Dataplane) +parameters: + image: + type: string + label: Image name or ID + description: Image to be used for compute instance + default: RapidVM + flavor: + type: string + label: Flavor + description: Type of instance (flavor) to be used + default: prox_flavor + key: + type: string + label: Key name + description: Name of key-pair to be used for compute instance + default: prox + dpdk_network: + type: string + label: Private network name or ID + description: Network to attach instance to. + default: dpdk-network + private_network: + type: string + label: Private network name or ID + description: Network to attach instance to. + default: admin_internal_net + availability_zone: + type: string + description: The Availability Zone to launch the instance. + default: nova + +resources: + sut: + type: OS::Nova::Server + properties: + availability_zone: { get_param: availability_zone } + user_data: + get_file: prox_sut_user_data.sh + key_name: { get_param: key } + image: { get_param: image } + flavor: { get_param: flavor } + networks: + - network: { get_param: private_network } + - network: { get_param: dpdk_network } + gen: + type: OS::Nova::Server + properties: + availability_zone: { get_param: availability_zone } + user_data: + get_file: prox_gen_user_data.sh + key_name: { get_param: key } + image: { get_param: image } + flavor: { get_param: flavor } + networks: + - network: { get_param: private_network } + - network: { get_param: dpdk_network } + + sut_floating_ip: + type: OS::Nova::FloatingIP + properties: + pool: admin_floating_net + + gen_floating_ip: + type: OS::Nova::FloatingIP + properties: + pool: admin_floating_net + + sut_association: + type: OS::Nova::FloatingIPAssociation + properties: + floating_ip: { get_resource: sut_floating_ip } + server_id: { get_resource: sut } + + gen_association: + type: OS::Nova::FloatingIPAssociation + properties: + floating_ip: { get_resource: gen_floating_ip } + server_id: { get_resource: gen } + +outputs: + sut_ip: + description: IP address of the instance + value: { get_attr: [sut, first_address] } + gen_ip: + description: IP address of the instance + value: { get_attr: [gen, first_address] } + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg new file mode 100644 index 00000000..2937a749 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg @@ -0,0 +1,51 @@ +;; +;; Copyright (c) 2010-2017 Intel Corporation +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[lua] +dofile("parameters.lua") + +[port 0] +name=if0 +mac=hardware + +[defaults] +mempool size=2K + +[global] +name=NOP forwarding + +[core 0] +mode=master + +[core 1] +name=swap +task=0 +mode=arp +sub mode=local +rx port=if0 +tx port=if0 +tx cores=1t1 +local ipv4=${sut_ip} +task=1 +mode=swap +rx ring=yes +tx port=if0 +drop=no + diff --git a/VNFs/DPPD-PROX/helper-scripts/start_vm.py b/VNFs/DPPD-PROX/helper-scripts/start_vm.py new file mode 100755 index 00000000..7af7df9c --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/start_vm.py @@ -0,0 +1,143 @@ +#!/bin/env python2.7 + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +from os import system +from os import fork, _exit +from subprocess import check_output +import socket +from time import sleep +import json +import sys + +# This script starts qemu with the CPU layout specified by the cores +# array below. Each element in the array represents a core. To enable +# hyper-threading (i.e. two logical cores per core), each element in +# the array should be an array of length two. The values stored inside +# the array define to which host cores the guest cores should be +# affinitized. All arguments of this script are passed to qemu +# directly. Porting an existing qemu command line setup to make use of +# this script requires removing the -smp parameters and -qmp +# parameters if those were used. These are built by the script based +# on the cores array. + +# After successfully starting qemu, this script will connect through +# QMP and affinitize all cores within the VM to match cores on the +# host. + +execfile("./vm-cores.py") + +def build_mask(cores): + ret = 0; + for core in cores: + for thread in core: + ret += 1 << thread; + return ret; + +n_cores = len(cores); +n_threads = len(cores[0]); + +mask = str(hex((build_mask(cores)))) + +smp_str = str(n_cores*n_threads) +smp_str += ",cores=" + str(n_cores) +smp_str += ",sockets=1" +smp_str += ",threads=" + str(n_threads) + +try: + qmp_sock = check_output(["mktemp", "--tmpdir", "qmp-sock-XXXX"]).strip() +except: + qmp_sock = "/tmp/qmp-sock" + +qemu_cmdline = "" +qemu_cmdline += "taskset " + mask + " qemu-system-x86_64 -smp " + smp_str +qemu_cmdline += " -qmp unix:" + qmp_sock + ",server,nowait" +qemu_cmdline += " -daemonize" + +for a in sys.argv[1:]: + qemu_cmdline += " " + a + +try: + pid = fork() +except OSError, e: + sys.exit("Failed to fork: " + e.strerror) + +if (pid != 0): + # In the parent process + ret = system(qemu_cmdline) + if (ret != 0): + sys.exit("Failed to run QEMU: exit status " + str(ret) + ". Command line was:\n" + qemu_cmdline) + # Parent process done + sys.exit(0) + +# In the child process: use _exit to terminate +retry = 0 +s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +while (retry < 10): + sleep(1); + try: + s.connect(qmp_sock) + print "Connected to QMP" + break; + except: + pass + retry = retry + 1 + print "Failed to connect to QMP, attempt " + str(retry) +if (retry >= 10): + print "Failed to connect to QMP" + _exit(1) + +# skip info about protocol +dat = s.recv(100000) +# need to run qmp_capabilities before next command works +s.send("{\"execute\" : \"qmp_capabilities\" }") +dat = s.recv(100000) +# Get the PID for each guest core +s.send("{\"execute\" : \"query-cpus\"}") +dat = s.recv(100000) +a = json.loads(dat)["return"]; + +if (len(a) != n_cores*n_threads): + print "Configuration mismatch: " + str(len(a)) + " vCPU reported by QMP, instead of expected " + str(n_cores*n_threads) + _exit(1) +print "QMP reported " + str(len(a)) + " vCPU, as expected" + +if (n_threads == 1): + idx = 0; + for core in a: + cm = str(hex(1 << cores[idx][0])) + pid = str(core["thread_id"]) + system("taskset -p " + cm + " " + pid + " > /dev/null") + idx = idx + 1 +elif (n_threads == 2): + idx = 0; + prev = 0; + for core in a: + cm = str(hex(1 << cores[idx][prev])) + pid = str(core["thread_id"]) + system("taskset -p " + cm + " " + pid + " > /dev/null") + prev = prev + 1; + if (prev == 2): + idx = idx + 1; + prev = 0 +else: + print "Not implemented yet: more than 2 threads per core" + _exit(1) + +print "Core affinitization completed" +_exit(0) + diff --git a/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_BNG_8ports.py b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_BNG_8ports.py new file mode 100755 index 00000000..f26d0db6 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_BNG_8ports.py @@ -0,0 +1,457 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import socket +import sys +import os +from time import * +from datetime import datetime +from optparse import OptionParser +import time +from remote_system import * +from math import log + +# General parameters +accuracy = 0.1 # in percent of line rate +max_dropped = 0.1 # in percent +all_pkt_size = [64,128,256,512,1024,1280,1494] +all_ip_src = [0,6,12,18] +all_ip_dst = [0,6,12,18] + +# Stear parameters +step_time = 0.001 # in seconds +step_delta = 10 # in percent of line rate + +##### Use case 1: packet loss and latency ##### +low_steps_delta_for_loss = 0.01 # Use increment of 0.01% from 0 to low_steps +medium_steps_delta_for_loss = 0.1 # Use increment of 0.1% from low_steps to medium_steps +normal_steps_delta_for_loss = 1.0 # Use increment of 1% from medium_steps till 100% +low_steps = 0.1 +medium_steps = 1.0 + +# Prox parameters +tx_port0 = [4] +tx_port1 = [6] +tx_port2 = [8] +tx_port3 = [10] +tx_port4 = [12] +tx_port5 = [14] +tx_port6 = [16] +tx_port7 = [18] +tx_task = 0 + +all_rx_cores = [20,22,24,26,28,30,32,34] +rx_lat_cores = [20,22,24,26,28,30,32,34] +rx_task = 0 + +# Some variables, do not change + +# Program arguments +parser = OptionParser() +parser.add_option("-d", "--duration", dest="test_duration", help="Duration of each steps", metavar="integer", default=10) +parser.add_option("-s", "--speed", dest="init_speed", help="Initial speed", metavar="integer", default=100) +parser.add_option("-r", "--run", dest="run", help="Run test", metavar="integer", default=0) +parser.add_option("-c", "--configure", dest="configure", help="Configure Test", metavar="integer", default=0) +(options, args) = parser.parse_args() + +init_speed = int(options.init_speed) +test_duration = int(options.test_duration) +configure = int(options.configure) +run = int(options.run) + +nb_cores_per_interface = len(tx_port0) +max_speed = (100.0/nb_cores_per_interface) +init_speed = (init_speed * 1.0/nb_cores_per_interface) +accuracy = (accuracy * 1.0/nb_cores_per_interface) +normal_steps_delta_for_loss = (normal_steps_delta_for_loss /nb_cores_per_interface) +medium_steps_delta_for_loss = (medium_steps_delta_for_loss /nb_cores_per_interface) +low_steps_delta_for_loss = (low_steps_delta_for_loss /nb_cores_per_interface) +medium_steps = (medium_steps /nb_cores_per_interface) +low_steps = (low_steps /nb_cores_per_interface) + +max_dropped = max_dropped / 100 + +def to_str(arr): + ret = "" + first = 1; + for a in arr: + if (first == 0): + ret += "," + + ret += str(a) + first = 0; + return ret; + +tx_cores_cpe = tx_port0 + tx_port1 + tx_port2 + tx_port3 +tx_cores_inet = tx_port4 + tx_port5 + tx_port6 + tx_port7 +tx_cores = tx_cores_cpe + tx_cores_inet + +def send_all_pkt_size(cores, pkt_size): + for c in cores: + sock.sendall("pkt_size " + str(c) + " 0 " + str(pkt_size) + "\n"); + +def send_all_value(cores, offset, value, len): + for c in cores: + sock.sendall("set value " + str(c) + " 0 " + str(offset) + " " + str(value) + " " + str(len)+ "\n"); + +def send_all_random(cores, offset, rand_str, len): + for c in cores: + sock.sendall("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + #print("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + +def send_all_speed(cores, speed_perc): + for c in cores: + sock.sendall("speed " + str(c) + " 0 " + str(speed_perc) + "\n"); + +def send_reset_random(): + sock.sendall("reset randoms all" + "\n"); + +def send_reset_value(): + sock.sendall("reset values all" + "\n"); + +def rx_stats(tx_cores, tx_task, rx_cores, rx_task): + rx = tx = drop = tsc = tsc_hs = ierrors = 0 + for e in tx_cores: + sock.sendall("core stats " + str(e) + " " + str(tx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + for e in rx_cores: + sock.sendall("core stats " + str(e) + " " + str(rx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + # Also get the ierrors as generators might be the bottleneck... + sock.sendall("tot ierrors tot\n") + recv = recv_once() + ierrors += int(recv.split(",")[0]) + rx+=ierrors + return rx,tx,drop,tsc,tsc_hz + +def lat_stats(cores,task): + lat_min = [0 for e in range(127)] + lat_max = [0 for e in range(127)] + lat_avg = [0 for e in range(127)] + for e in cores: + sock.sendall("lat stats " + str(e) + " " + str(task) + " " + "\n") + recv = recv_once() + lat_min[e] = int(recv.split(",")[0]) + lat_max[e] = int(recv.split(",")[1]) + lat_avg[e] = int(recv.split(",")[2]) + return lat_min, lat_max, lat_avg + +def recv_once(): + ret_str = ""; + done = 0; + while done == 0: + dat = sock.recv(256); + i = 0; + while(i < len(dat)): + if (dat[i] == '\n'): + done = 1 + else: + ret_str += dat[i]; + i = i + 1; + return ret_str + +def set_pkt_sizes(tx_cores, p): + send_all_pkt_size(tx_cores, p-4) + # For all cores, need to adapt IP Length (byte 16) and UDP Length (byte 38) to pkt size + send_all_value(tx_cores, 16, p - 18, 2) # 14 for MAC (12) EthType (2) + send_all_value(tx_cores, 38, p - 38, 2) # 34 for MAC (12) EthType (2) IP (20) + +def set_pkt_sizes_cpe(tx_cores, p): + send_all_pkt_size(tx_cores, p-4) + # For all cores, need to adapt IP Length (byte 16) and UDP Length (byte 38) to pkt size + send_all_value(tx_cores, 24, p - 26, 2) # 22 for QinQ (8) MAC (12) EthType (2) + send_all_value(tx_cores, 46, p - 46, 2) # 42 for QinQ (8) MAC (12) EthType (2) IP (20) + +def set_pkt_sizes_inet(tx_cores, p): + send_all_pkt_size(tx_cores, p+24-4) + # For all cores, need to adapt IP Length (byte 16) and UDP Length (byte 38) to pkt size + send_all_value(tx_cores, 20, p + 2, 2) # 14 for MAC (12) EthType (2) + send_all_value(tx_cores, 48, p - 26, 2) # 14 for MAC (12) EthType (2) + send_all_value(tx_cores, 70, p - 46, 2) # 34 for MAC (12) EthType (2) IP (20) + +def run_measure_throughput(speed, speed_cpe): + done = 0 + # Intialize tests by stopping cores and resetting stats + step=0 + steps_done = 0 + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(2) + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + sock.sendall("reset stats\n") + print "Speed = " + str(speed * nb_cores_per_interface) + sleep(1); + + send_all_speed(tx_cores, step); + + # Now starting the steps. First go to the common speed, then increase steps for the faster one. + sock.sendall("start " + to_str(tx_cores) + "," + to_str(rx_lat_cores) + "\n") + while (steps_done == 0): + sleep(step_time) + if (step + step_delta <= speed): + step+=step_delta + else: + steps_done = 1; + send_all_speed(tx_cores, step) + + # Steps are now OK. Set speed + send_all_speed(tx_cores_inet, speed); + send_all_speed(tx_cores_cpe, speed_cpe); + sleep(2); + + # Getting statistics to calculate PPS at right speed.... + rx_pps_beg,tx_pps_beg,drop_pps_beg,tsc_pps_beg,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + sleep(test_duration); + + # Collect statistics before test stops...and stop the test. Important to get stats before stopping as stops take some time... + rx_pps_end,tx_pps_end,drop_pps_end,tsc_pps_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + lat_min,lat_max,lat_avg = lat_stats(rx_lat_cores, rx_task) + sock.sendall("stop " + to_str(tx_cores) + "\n") + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(3); + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + + rx_end, tx_end,drop_end,tsc_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + rx = rx_pps_end - rx_pps_beg + tsc = tsc_pps_end - tsc_pps_beg + mpps = rx / (tsc/float(tsc_hz)) / 1000000 + tx = tx_pps_end - tx_pps_beg + tx_mpps = tx / (tsc/float(tsc_hz)) / 1000000 + + #print "Runtime = " + str((tsc)/float(tsc_hz)); + if (tx_end == 0): + dropped_tot = tx_end - rx_end + dropped_pct = 0 + else: + dropped_tot = tx_end - rx_end + dropped_pct = ((dropped_tot) * 1.0) / tx_end + + if (dropped_tot > 0): + if (dropped_pct >= max_dropped): + print "** FAILED **: lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + print "OK but lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + if (dropped_tot < 0): + print "Something wrong happened - received more packets than transmitted" + else: + print "** OK **: RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + print "MPPS = " + str(mpps) + print "====================================================" + return dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg + +def write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_flows, lat_min, lat_max, lat_avg): + f.write(str(pkt_size) + "; " + str(tx_mpps) + "; " + str(mpps) + "; " + str(100 * dropped_pct) + "; " + str(dropped_tot) + "; " + str(speed * nb_cores_per_interface) + "; " + str(number_flows) + "; " ) + for e in rx_lat_cores: + f.write(str(lat_min[e]) + "; " + str(lat_max[e]) + "; " + str(lat_avg[e]) + "; ") + f.write("\n"); + f.flush() + +def run_dicho_search(number_flows, pkt_size): + previous_success_speed = 0.0 + previous_error_speed = max_speed + speed = init_speed * 1.0 + done = 0; + good_tx_mpps = 0 + good_mpps = 0 + good_dropped_pct = 0 + good_dropped_tot = 0 + good_speed = 0 + good_lat_min = [0 for e in range(127)] + good_lat_max = [0 for e in range(127)] + good_lat_avg = [0 for e in range(127)] + + while done == 0: + speed_cpe = (speed * (pkt_size + 20)) / (pkt_size + 24 + 20) + dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg = run_measure_throughput(speed, speed_cpe) + if ((dropped_tot >= 0) and (dropped_pct <= max_dropped)): + good_tx_mpps = tx_mpps + good_mpps = mpps + good_dropped_pct = dropped_pct + good_dropped_tot = dropped_tot + good_speed = speed + good_lat_min = lat_min + good_lat_max = lat_max + good_lat_avg = lat_avg + write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_flows, lat_min, lat_max, lat_avg); + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_flows, lat_min, lat_max, lat_avg); + else: + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_flows, lat_min, lat_max, lat_avg); + + if ((speed == max_speed) and (dropped_pct <= max_dropped)): + write_results(f_minimal, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_flows, lat_min, lat_max, lat_avg); + done = 1 + if (dropped_pct <= max_dropped): + previous_success_speed = speed + if (speed > max_speed - accuracy): + speed = max_speed + else: + if (previous_error_speed - speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_flows, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed + (previous_error_speed - speed)/2; + else: + previous_error_speed = speed + if (speed - previous_success_speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_flows, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed - (speed - previous_success_speed) / 2; + + +def set_source_destination_ip(nb_sources, nb_destinations): + # Destination addressese: "00XXXXXX" "XXXXXXXX" "XXXXXXXX" "XXXXXX10" + # Starting with 00 to be in class A and skipping 0.x.y.z and 127.x.y.z + # Ending with 10 to avoid x.y.z.0 and x.y.z.255 + + dst_mask = "10" + for i in range (nb_destinations): + dst_mask = "X" + str(dst_mask) + for i in range (32 - nb_destinations - 2): + dst_mask = "0" + str(dst_mask) + + src_mask = "10" + for i in range (nb_sources): + src_mask = "X" + str(src_mask) + for i in range (32 - nb_sources - 2): + src_mask = "0" + str(src_mask) + + for c in tx_port0: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port1: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port2: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port3: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port4: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port5: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port6: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + for c in tx_port7: + send_all_random([c], 26, src_mask, 4) + send_all_random([c], 30, dst_mask, 4) + +#======================================================================== +class TestDefinition(): + "Stores test parameters" + def __init__(self, number_ip_src, number_ip_dst, pkt_size): + self.number_ip_src = number_ip_src + self.number_ip_dst = number_ip_dst + self.pkt_size = pkt_size + +#======================================================================== +def run_use_case(number_ip_src, number_ip_dst, pkt_size): + number_flows = (2 ** number_ip_src) * (2 ** number_ip_dst) +# send_reset_random() +# send_reset_value() +# set_source_destination_ip(number_ip_src, number_ip_dst) + set_pkt_sizes_inet(tx_cores_inet, pkt_size) + set_pkt_sizes_cpe(tx_cores_cpe, pkt_size) + print "Running test with pkt size= " + str(pkt_size) + " number_ip_src = " + str(number_ip_src) + " number_ip_dst = " + str(number_ip_dst) + " Number flows = " + str(number_flows) + "; \n" + run_dicho_search(number_flows, pkt_size) + sleep(3) + +#======================================================================== +def run_all_use_cases(): + use_case_nb = 1 + # Connect to dppd + file_path = '/tmp/prox.sock' + sock.connect(file_path) + + f.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_all.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_minimal.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f.flush(); + f_all.flush(); + f_minimal.flush(); + + # Starting tests + print "Stopping all cores and resetting all values and randoms before starting\n" + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + sock.sendall("stop " + to_str(tx_cores) + "\n") + #sock.sendall("stop all") + sock.sendall("reset stats\n") + sleep(3); + for line in file_tests: + info = line.split(';') + if (info[0][0] == '#'): + continue + if (info[0][0] == ''): + break + number_ip_src = int(info[0]) + number_ip_dst = int(info[1]) + pkt_size = int(info[2]) + run_use_case(number_ip_src, number_ip_dst, pkt_size) + +#======================================================================== +def configure_use_case(): + Tests = [] + number_ip_dst = 0 + number_ip_src = 0 + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(number_ip_src, number_ip_dst, pkt_size)) + + pkt_size = 64 + while (pkt_size < 1494): + Tests.append(TestDefinition(number_ip_src, number_ip_dst, pkt_size)) + pkt_size = (pkt_size *11) / 10 + + file_tests = open('test_description.txt', 'w') + file_tests.write("# Number_ip_src; number_ip_dst; pkt_size; \n") + for test in Tests: + file_tests.write(str(test.number_ip_src) + "; " + str(test.number_ip_dst) + "; " + str(test.pkt_size) + "; " + ";\n") + file_tests.close() + +#======================================================================== +if ((configure == 0) and (run == 0)): + print "Nothing to do - please use -r 1 or -c 1" +if (configure == 1): + configure_use_case() +if (run == 1): + print "****************************************************************************************************************" + print "** Running Characterization with " + str(test_duration) + " seconds steps and starting at " + str(init_speed) + " percent of line rate **" + print "****************************************************************************************************************" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + f_all = open('all_results.txt', 'w') + f = open('detailed_results.txt', 'w') + f_minimal = open('minimal_results.txt', 'w') + file_tests = open('test_description.txt', 'r') + run_all_use_cases() + f.close(); + sock.close(); + diff --git a/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter.py b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter.py new file mode 100755 index 00000000..f4d211f6 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter.py @@ -0,0 +1,681 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import socket +import sys +import os +from time import * +from datetime import datetime +from optparse import OptionParser +import time +from remote_system import * +from math import log + +# General parameters +accuracy = 0.1 # in percent of line rate +max_dropped = 0.001 # in percent +all_pkt_size = [64,128,256,512,1024,1280,1518] +#all_pkt_size = [64] + +# vRouter parameters, in case commands must be sent +vRouter_host = "192.168.1.96" + +# Stear parameters +step_time = 0.01 # in seconds +step_delta = 0.025 # in percent of line rate + +# Use case dependent parameters +##### Use case 0: influence of number of routes and next hops ##### +max_number_next_hops = 256 # Maximum number of next-hops per interface +max_number_routes = 8192 # Maximum number of routes per interface +max_number_addresses_local_network = 262144 + +##### Use case 1: packet loss and latency ##### +low_steps_delta_for_loss = 0.01 # Use increment of 0.01% from 0 to low_steps +medium_steps_delta_for_loss = 0.1 # Use increment of 0.1% from low_steps to medium_steps +normal_steps_delta_for_loss = 1.0 # Use increment of 1% from medium_steps till 100% +low_steps = 0.1 +medium_steps = 1.0 + +# Prox parameters +tx_port4 = [19,27,55,63] +tx_port5 = [20,28,56,64] +tx_port6 = [21,29,57,65] +tx_port7 = [22,30,58,66] +tx_port2 = [23,31,59,67] +tx_port3 = [24,32,60,68] +tx_port0 = [25,33,61,69] +tx_port1 = [26,34,62,70] +tx_task = 0 + +all_rx_cores = [1,2,3,4,5,6,7,10] +rx_lat_cores = [1,2,3,4,5,6,7,10] +rx_task = 1 + +# Some variables, do not change + +# Program arguments +parser = OptionParser() +parser.add_option("-d", "--duration", dest="test_duration", help="Duration of each steps", metavar="integer", default=10) +parser.add_option("-s", "--speed", dest="init_speed", help="Initial speed", metavar="integer", default=100) +parser.add_option("-u", "--use-case", dest="use_case", help="Use Case Number", metavar="integer", default=0) +parser.add_option("-r", "--run", dest="run", help="Run test", metavar="integer", default=0) +parser.add_option("-c", "--configure", dest="configure", help="Configure Test", metavar="integer", default=0) +(options, args) = parser.parse_args() + +init_speed = int(options.init_speed) +test_duration = int(options.test_duration) +use_case = int(options.use_case) +configure = int(options.configure) +run = int(options.run) + +nb_cores_per_interface = len(tx_port0) +max_speed = (100.0/nb_cores_per_interface) +init_speed = (init_speed * 1.0/nb_cores_per_interface) +accuracy = (accuracy * 1.0/nb_cores_per_interface) +normal_steps_delta_for_loss = (normal_steps_delta_for_loss /nb_cores_per_interface) +medium_steps_delta_for_loss = (medium_steps_delta_for_loss /nb_cores_per_interface) +low_steps_delta_for_loss = (low_steps_delta_for_loss /nb_cores_per_interface) +medium_steps = (medium_steps /nb_cores_per_interface) +low_steps = (low_steps /nb_cores_per_interface) + +max_dropped = max_dropped / 100 + +def to_str(arr): + ret = "" + first = 1; + for a in arr: + if (first == 0): + ret += "," + + ret += str(a) + first = 0; + return ret; + +tx_cores = tx_port0 + tx_port1 + tx_port2 + tx_port3 + tx_port4 + tx_port5 + tx_port6 + tx_port7 + +def send_all_pkt_size(cores, pkt_size): + for c in cores: + sock.sendall("pkt_size " + str(c) + " 0 " + str(pkt_size) + "\n"); + +def send_all_value(cores, offset, value, len): + for c in cores: + sock.sendall("set value " + str(c) + " 0 " + str(offset) + " " + str(value) + " " + str(len)+ "\n"); + +def send_all_random(cores, offset, rand_str, len): + for c in cores: + sock.sendall("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + #print("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + +def send_all_speed(cores, speed_perc): + for c in cores: + sock.sendall("speed " + str(c) + " 0 " + str(speed_perc) + "\n"); + +def send_reset_random(): + sock.sendall("reset randoms all" + "\n"); + +def send_reset_value(): + sock.sendall("reset values all" + "\n"); + +def rx_stats(tx_cores, tx_task, rx_cores, rx_task): + rx = tx = drop = tsc = tsc_hs = ierrors = 0 + for e in tx_cores: + sock.sendall("core stats " + str(e) + " " + str(tx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + for e in rx_cores: + sock.sendall("core stats " + str(e) + " " + str(rx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + # Also get the ierrors as generators might be the bottleneck... + sock.sendall("tot ierrors tot\n") + recv = recv_once() + ierrors += int(recv.split(",")[0]) + rx+=ierrors + return rx,tx,drop,tsc,tsc_hz + +def lat_stats(cores,task): + lat_min = [0 for e in range(127)] + lat_max = [0 for e in range(127)] + lat_avg = [0 for e in range(127)] + for e in cores: + sock.sendall("lat stats " + str(e) + " " + str(task) + " " + "\n") + recv = recv_once() + lat_min[e] = int(recv.split(",")[0]) + lat_max[e] = int(recv.split(",")[1]) + lat_avg[e] = int(recv.split(",")[2]) + return lat_min, lat_max, lat_avg + +def recv_once(): + ret_str = ""; + done = 0; + while done == 0: + dat = sock.recv(256); + i = 0; + while(i < len(dat)): + if (dat[i] == '\n'): + done = 1 + else: + ret_str += dat[i]; + i = i + 1; + return ret_str + +def wait_vRouter_restarted(host): + while (1): + ret = os.system("ping " + host + " -c 1 > /dev/null") + if ret == 0: + print "still up..." + else: + break; + sleep(1) + + while (1): + ret = os.system("ping " + host + " -c 1 > /dev/null") + if (ret == 0): + print "UP" + break; + else: + print "still down..." + sleep(1) + +def reload_vRouter_config(config): + print "connecting to vRouter...and copying " + str(config) + sut = remote_system("root", vRouter_host) + cmd = "cp /config/prox/" + str(config) + " /config/config.boot" + sut.run(cmd) + print "Rebooting system at " + str(datetime.now().time()) + sut.run_forked("reboot") + sleep(5) + wait_vRouter_restarted(vRouter_host) + print "Waiting for last startup scripts to start..." + last_script = "l2tp" + while(1): + dmesg = str(sut.run("dmesg")) + if last_script in dmesg: + print "found l2tp - UP" + break; + sleep(1) + print "vRouter started - waiting 5 last seconds before starting test" + sleep(5) + print datetime.now().time() + +def set_pkt_sizes(tx_cores, p): + send_all_pkt_size(tx_cores, p-4) + # For all cores, need to adapt IP Length (byte 16) and UDP Length (byte 38) to pkt size + send_all_value(tx_cores, 16, p - 18, 2) # 14 for MAC (12) EthType (2) + send_all_value(tx_cores, 38, p - 38, 2) # 34 for MAC (12) EthType (2) IP (20) + +def run_measure_throughput(speed): + done = 0 + # Intialize tests by stopping cores and resetting stats + step=0 + steps_done = 0 + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(2) + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + sock.sendall("reset stats\n") + print "Speed = " + str(speed * nb_cores_per_interface) + sleep(1); + + send_all_speed(tx_cores, step); + + # Now starting the steps. First go to the common speed, then increase steps for the faster one. + sock.sendall("start " + to_str(tx_cores) + "," + to_str(rx_lat_cores) + "\n") + while (steps_done == 0): + sleep(step_time) + if (step + step_delta <= speed): + step+=step_delta + else: + steps_done = 1; + send_all_speed(tx_cores, step) + + # Steps are now OK. Set speed + send_all_speed(tx_cores, speed); + sleep(2); + + # Getting statistics to calculate PPS at right speed.... + rx_pps_beg,tx_pps_beg,drop_pps_beg,tsc_pps_beg,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + sleep(test_duration); + + # Collect statistics before test stops...and stop the test. Important to get stats before stopping as stops take some time... + rx_pps_end,tx_pps_end,drop_pps_end,tsc_pps_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + lat_min,lat_max,lat_avg = lat_stats(rx_lat_cores, rx_task) + sock.sendall("stop " + "," + to_str(tx_cores) + "\n") + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(3); + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + + rx_end, tx_end,drop_end,tsc_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + rx = rx_pps_end - rx_pps_beg + tsc = tsc_pps_end - tsc_pps_beg + mpps = rx / (tsc/float(tsc_hz)) / 1000000 + tx = tx_pps_end - tx_pps_beg + tx_mpps = tx / (tsc/float(tsc_hz)) / 1000000 + + #print "Runtime = " + str((tsc)/float(tsc_hz)); + if (tx_end == 0): + dropped_tot = tx_end - rx_end + dropped_pct = 0 + else: + dropped_tot = tx_end - rx_end + dropped_pct = ((dropped_tot) * 1.0) / tx_end + + if (dropped_tot > 0): + if (dropped_pct >= max_dropped): + print "** FAILED **: lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + print "OK but lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + if (dropped_tot < 0): + print "Something wrong happened - received more packets than transmitted" + else: + print "** OK **: RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + print "MPPS = " + str(mpps) + print "====================================================" + return dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg + +def write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg): + f.write(str(pkt_size) + "; " + str(tx_mpps) + "; " + str(mpps) + "; " + str(100 * dropped_pct) + "; " + str(dropped_tot) + "; " + str(speed * nb_cores_per_interface) + "; " + str(number_next_hops) + "; " + str(number_routes) + "; " + str(traffic) + "; ") + for e in rx_lat_cores: + f.write(str(lat_min[e]) + "; " + str(lat_max[e]) + "; " + str(lat_avg[e]) + "; ") + f.write("\n"); + f.flush() + +def run_loss_graph(number_next_hops, number_routes, pkt_size, traffic): + speed = init_speed * 1.0 + done = 0; + while done == 0: + dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg = run_measure_throughput(speed) + write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + if (speed <= low_steps_delta_for_loss): + done = 1 + return + if (speed >= (medium_steps+normal_steps_delta_for_loss)): + speed -= normal_steps_delta_for_loss + else: + if (speed >= (low_steps+medium_steps_delta_for_loss)): + speed -= medium_steps_delta_for_loss + else: + speed -= low_steps_delta_for_loss + +def run_dicho_search(number_next_hops, number_routes, pkt_size, traffic): + previous_success_speed = 0.0 + previous_error_speed = max_speed + speed = init_speed * 1.0 + done = 0; + good_tx_mpps = 0 + good_mpps = 0 + good_dropped_pct = 0 + good_dropped_tot = 0 + good_speed = 0 + good_lat_min = [0 for e in range(127)] + good_lat_max = [0 for e in range(127)] + good_lat_avg = [0 for e in range(127)] + + while done == 0: + dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg = run_measure_throughput(speed) + if ((dropped_tot >= 0) and (dropped_pct <= max_dropped)): + good_tx_mpps = tx_mpps + good_mpps = mpps + good_dropped_pct = dropped_pct + good_dropped_tot = dropped_tot + good_speed = speed + good_lat_min = lat_min + good_lat_max = lat_max + good_lat_avg = lat_avg + write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + else: + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + + if ((speed == max_speed) and (dropped_pct <= max_dropped)): + write_results(f_minimal, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + done = 1 + if (dropped_pct <= max_dropped): + previous_success_speed = speed + if (speed > max_speed - accuracy): + speed = max_speed + else: + if (previous_error_speed - speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed + (previous_error_speed - speed)/2; + else: + previous_error_speed = speed + if (speed - previous_success_speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed - (speed - previous_success_speed) / 2; + + +def set_destination_ip(use_case, nb_destinations, traffic): + # minimmum 8 routes i.e. 1 per interface + # Destination addressese: "00XXXYY1" "Z00ZZ0ZZ" "AA0AA0AA" "BBBBBB10" + # Where X = interface id. Starting with 00 to be in class A and skipping 0.x.y.z and 127.x.y.z + # Y, Z and A = additional routes + # B = IP in routes. 10 to avoid x.y.z.0 and x.y.z.255 + # Gaps in A and B to void "too good" distributions e.g. using LPM and + # First changing Y + + mask = "" + for i in range (2): + mask = str(mask)+"0" + end_mask = "" + if (use_case != 2): + end_mask = "XXXXXX10" # Last 8 bits + + if (nb_destinations == 1): + end_mask = "0010000000000000000" + str(end_mask) + if (nb_destinations == 2): + end_mask = "X010000000000000000" + str(end_mask) + if (nb_destinations == 4): + end_mask = "XX10000000000000000" + str(end_mask) + if (nb_destinations == 8): + end_mask = "XX1X000000000000000" + str(end_mask) + elif (nb_destinations == 16): + end_mask = "XX1X00X000000000000" + str(end_mask) + elif (nb_destinations == 32): + end_mask = "XX1X00XX00000000000" + str(end_mask) + elif (nb_destinations == 64): + end_mask = "XX1X00XX0X000000000" + str(end_mask) + elif (nb_destinations == 128): + end_mask = "XX1X00XX0XX00000000" + str(end_mask) + elif (nb_destinations == 256): + end_mask = "XX1X00XX0XXX0000000" + str(end_mask) + elif (nb_destinations == 512): + end_mask = "XX1X00XX0XXXX000000" + str(end_mask) + elif (nb_destinations == 1024): + end_mask = "XX1X00XX0XXXX0X0000" + str(end_mask) + elif (nb_destinations == 2048): + end_mask = "XX1X00XX0XXXX0XX000" + str(end_mask) + elif (nb_destinations == 4096): + end_mask = "XX1X00XX0XXXX0XX0X0" + str(end_mask) + elif (nb_destinations == 8192): + end_mask = "XX1X00XX0XXXX0XX0XX" + str(end_mask) + else: + if (nb_destinations <= 64 * 1): + end_mask = "0010000000000000000" + n_dest = int(log(nb_destinations, 2)) + for i in range (n_dest): + end_mask = str(end_mask) + "X" + for i in range (6 - n_dest): + end_mask = str(end_mask) + "0" + end_mask = str(end_mask) + "10" + else: + end_mask = "XXXXXX10" # Last 8 bits + + if (nb_destinations == 64 * 2): + end_mask = "001X000000000000000" + str(end_mask) + elif (nb_destinations == 64 * 4): + end_mask = "001X00X000000000000" + str(end_mask) + elif (nb_destinations == 64 * 8): + end_mask = "001X00XX00000000000" + str(end_mask) + elif (nb_destinations == 64 * 16): + end_mask = "001X00XX0X000000000" + str(end_mask) + elif (nb_destinations == 64 * 32): + end_mask = "001X00XX0XX00000000" + str(end_mask) + elif (nb_destinations == 64 * 64): + end_mask = "001X00XX0XXX0000000" + str(end_mask) + elif (nb_destinations == 64 * 128): + end_mask = "001X00XX0XXXX000000" + str(end_mask) + elif (nb_destinations == 64 * 256): + end_mask = "001X00XX0XXXX0X0000" + str(end_mask) + elif (nb_destinations == 64 * 512): + end_mask = "001X00XX0XXXX0XX000" + str(end_mask) + elif (nb_destinations == 64 * 1024): + end_mask = "001X00XX0XXXX0XX0X0" + str(end_mask) + elif (nb_destinations == 64 * 2048): + end_mask = "001X00XX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 4096): + end_mask = "001XX0XX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 8192): + end_mask = "001XXXXX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 16384): + end_mask = "001XXXXXXXXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 32768): + end_mask = "001XXXXXXXXXXXXX0XX" + str(end_mask) + elif (nb_destinations == 64 * 65536): + end_mask = "001XXXXXXXXXXXXXXXX" + str(end_mask) + + if (traffic == 0): # One-to-one. From odd interface to even interface and vice versa, no QPI cross + mask1 = str(mask) + "001" + str(end_mask) + mask2 = str(mask) + "000" + str(end_mask) + mask3 = str(mask) + "011" + str(end_mask) + mask4 = str(mask) + "010" + str(end_mask) + mask5 = str(mask) + "101" + str(end_mask) + mask6 = str(mask) + "100" + str(end_mask) + mask7 = str(mask) + "111" + str(end_mask) + mask8 = str(mask) + "110" + str(end_mask) + + elif (traffic == 1): # Full mesh within QPI (i.e. 1 to 4) + mask1 = str(mask) + "0XX" + str(end_mask) + mask2 = str(mask) + "0XX" + str(end_mask) + mask3 = str(mask) + "0XX" + str(end_mask) + mask4 = str(mask) + "0XX" + str(end_mask) + mask5 = str(mask) + "1XX" + str(end_mask) + mask6 = str(mask) + "1XX" + str(end_mask) + mask7 = str(mask) + "1XX" + str(end_mask) + mask8 = str(mask) + "1XX" + str(end_mask) + + elif (traffic == 2): # One to one, crossing QPI (100% QPI) + mask1 = str(mask) + "100" + str(end_mask) + mask2 = str(mask) + "101" + str(end_mask) + mask3 = str(mask) + "110" + str(end_mask) + mask4 = str(mask) + "111" + str(end_mask) + mask5 = str(mask) + "000" + str(end_mask) + mask6 = str(mask) + "001" + str(end_mask) + mask7 = str(mask) + "010" + str(end_mask) + mask8 = str(mask) + "011" + str(end_mask) + + elif (traffic == 3): # 1 to 4 crossing QPI (100% QPI) + mask1 = str(mask) + "1XX" + str(end_mask) + mask2 = str(mask) + "1XX" + str(end_mask) + mask3 = str(mask) + "1XX" + str(end_mask) + mask4 = str(mask) + "1XX" + str(end_mask) + mask5 = str(mask) + "0XX" + str(end_mask) + mask6 = str(mask) + "0XX" + str(end_mask) + mask7 = str(mask) + "0XX" + str(end_mask) + mask8 = str(mask) + "0XX" + str(end_mask) + + elif (traffic == 4): # 1 to 4 (50% QPI) + mask1 = str(mask) + "XX1" + str(end_mask) + mask2 = str(mask) + "XX0" + str(end_mask) + mask3 = str(mask) + "XX1" + str(end_mask) + mask4 = str(mask) + "XX0" + str(end_mask) + mask5 = str(mask) + "XX1" + str(end_mask) + mask6 = str(mask) + "XX0" + str(end_mask) + mask7 = str(mask) + "XX1" + str(end_mask) + mask8 = str(mask) + "XX0" + str(end_mask) + + elif (traffic == 5): # Full mesh (50% QPI) + mask1 = str(mask) + "XXX" + str(end_mask) + mask2 = str(mask) + "XXX" + str(end_mask) + mask3 = str(mask) + "XXX" + str(end_mask) + mask4 = str(mask) + "XXX" + str(end_mask) + mask5 = str(mask) + "XXX" + str(end_mask) + mask6 = str(mask) + "XXX" + str(end_mask) + mask7 = str(mask) + "XXX" + str(end_mask) + mask8 = str(mask) + "XXX" + str(end_mask) + + for c in tx_port0: + send_all_random([c], 30, mask1, 4) + for c in tx_port1: + send_all_random([c], 30, mask2, 4) + for c in tx_port2: + send_all_random([c], 30, mask3, 4) + for c in tx_port3: + send_all_random([c], 30, mask4, 4) + for c in tx_port4: + send_all_random([c], 30, mask5, 4) + for c in tx_port5: + send_all_random([c], 30, mask6, 4) + for c in tx_port6: + send_all_random([c], 30, mask7, 4) + for c in tx_port7: + send_all_random([c], 30, mask8, 4) + for c in tx_cores: + send_all_random([c], 34, "0XXXXXXXXXXXXX10", 2) + send_all_random([c], 36, "0XXXXXXXXXXXXX10", 2) + +#======================================================================== +class TestDefinition(): + "Stores test parameters" + def __init__(self, use_case, next_hops, number_routes, pkt_size, traffic, reload): + self.use_case = use_case + self.next_hops = next_hops + self.number_routes = number_routes + self.pkt_size = pkt_size + self.traffic = traffic + self.reload = reload + +#======================================================================== +# Use case 0 increases input load and measure output load => show dropped packets at low loads, show overload behavior +# Use case 1 and use case 2 run dichotomic searches, searching for 0 packet loss (or whaever loss is configured) +# Use case 1 shows the effect of number of routes and next-hops +# Use case 2 shows the effect of the number of destination, using a fixed (low) number of routes and next-hops +#======================================================================== +def run_use_case(use_case, number_next_hops, number_routes, pkt_size, traffic, reload): + if (reload): + if (use_case == 2): + config = "config.1_1" + "_" + str(use_case) + ".boot" + else: + config = "config." + str(number_routes) + "_" + str(number_next_hops) + ".boot" + reload_vRouter_config(config) + send_reset_random() + send_reset_value() + set_destination_ip(use_case, number_routes, traffic) + set_pkt_sizes(tx_cores, pkt_size) + print "Running test with pkt size= " + str(pkt_size) + " Next hops = " + str(number_next_hops) + "; number of routes = " + str(number_routes) + "; Traffic = " + str(traffic) + " \n" + if (use_case == 0): + run_loss_graph(number_next_hops, number_routes, pkt_size, traffic) + else: + run_dicho_search(number_next_hops, number_routes, pkt_size, traffic) + sleep(3) + +#======================================================================== +def run_all_use_cases(): + use_case_nb = 1 + # Connect to dppd + file_path = '/tmp/prox.sock' + sock.connect(file_path) + + f.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_all.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_minimal.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f.flush(); + f_all.flush(); + f_minimal.flush(); + + # Starting tests + print "Stopping all cores and resetting all values and randoms before starting\n" + sock.sendall("stop all") + sock.sendall("reset stats\n") + sleep(3); + for line in file_tests: + info = line.split(';') + if (info[0][0] == '#'): + continue + if (info[0][0] == ''): + break + use_case = int(info[0]) + next_hops = int(info[1]) + number_routes = int(info[2]) + pkt_size = int(info[3]) + traffic = int(info[4]) + reload = int(info[5]) + print str(use_case_nb) + " : Running use case " + str(use_case) + " next_hops = " + str(next_hops) + " routes = " + str(number_routes) + " pkt_size = " + str(pkt_size) + " traffic = " + str(traffic) + " reload = " + str(reload) + run_use_case(use_case, next_hops, number_routes, pkt_size, traffic, reload) + use_case_nb = use_case_nb + 1 + +#======================================================================== +def configure_use_case(use_case): + Tests = [] + if (use_case == 0): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition("0", "1", "1", pkt_size, "0", "1")) + for pkt_size in all_pkt_size: + Tests.append(TestDefinition("0", "1", "1", pkt_size, "1", "1")) + if (use_case == 1): + number_next_hops = 1 + reload = 0 + + number_routes = number_next_hops # At least same number of routes that number of next hops + while number_routes <= max_number_routes: + reload = 1 + for traffic in range(6): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, number_routes, pkt_size, traffic, reload)) + reload = 0 + if (number_routes < max_number_routes / 2): + number_routes = number_routes * 4 + else: + number_routes = number_routes * 2 + + number_routes = max_number_next_hops + while number_next_hops <= max_number_next_hops: + reload = 1 + for traffic in range(6): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, number_routes, pkt_size, traffic, reload)) + reload = 0 + number_next_hops = number_next_hops * 2 + if (use_case == 2): + number_next_hops = 1 + reload = 1 + for traffic in range(6): + nb_destinations = 1 + while nb_destinations <= max_number_addresses_local_network: + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, nb_destinations, pkt_size, traffic, reload)) + reload = 0 + nb_destinations = nb_destinations * 2 + reload = 1 + + file_tests = open('test_description.txt', 'w') + file_tests.write("# Use case; next_hops; routes; pkt_size; traffic; reload;\n") + for test in Tests: + file_tests.write(str(test.use_case) + "; " + str(test.next_hops) + "; " + str(test.number_routes) + "; " + str(test.pkt_size) + "; " + str(test.traffic) + "; " + str(test.reload) + ";\n") + file_tests.close() + +#======================================================================== +if ((configure == 0) and (run == 0)): + print "Nothing to do - please use -r 1 or -c 1" +if (configure == 1): + configure_use_case(use_case) +if (run == 1): + print "****************************************************************************************************************" + print "** Running vRouter Characterization with " + str(test_duration) + " seconds steps and starting at " + str(init_speed) + " percent of line rate **" + print "****************************************************************************************************************" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + f_all = open('all_results.txt', 'w') + f = open('detailed_results.txt', 'w') + f_minimal = open('minimal_results.txt', 'w') + file_tests = open('test_description.txt', 'r') + run_all_use_cases() + f.close(); + sock.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter_4_ports.py b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter_4_ports.py new file mode 100755 index 00000000..95eb9811 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/testvRouter/characterize_vRouter_4_ports.py @@ -0,0 +1,681 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import socket +import sys +import os +from time import * +from datetime import datetime +from optparse import OptionParser +import time +from remote_system import * +from math import log + +# General parameters +accuracy = 0.1 # in percent of line rate +max_dropped = 0.001 # in percent +all_pkt_size = [64,128,256,512,1024,1280,1518] +#all_pkt_size = [64] + +# vRouter parameters, in case commands must be sent +vRouter_host = "192.168.1.96" + +# Stear parameters +step_time = 0.01 # in seconds +step_delta = 0.025 # in percent of line rate + +# Use case dependent parameters +##### Use case 0: influence of number of routes and next hops ##### +max_number_next_hops = 256 # Maximum number of next-hops per interface +max_number_routes = 8192 # Maximum number of routes per interface +max_number_addresses_local_network = 262144 + +##### Use case 1: packet loss and latency ##### +low_steps_delta_for_loss = 0.01 # Use increment of 0.01% from 0 to low_steps +medium_steps_delta_for_loss = 0.1 # Use increment of 0.1% from low_steps to medium_steps +normal_steps_delta_for_loss = 1.0 # Use increment of 1% from medium_steps till 100% +low_steps = 0.1 +medium_steps = 1.0 + +# Prox parameters +tx_port0 = [19,27,55,63] +tx_port1 = [20,28,56,64] +tx_port2 = [21,29,57,65] +tx_port3 = [22,30,58,66] +tx_port4 = [] +tx_port5 = [] +tx_port6 = [] +tx_port7 = [] +tx_task = 0 + +all_rx_cores = [23,24,25,26] +rx_lat_cores = [23,24,25,26] +rx_task = 1 + +# Some variables, do not change + +# Program arguments +parser = OptionParser() +parser.add_option("-d", "--duration", dest="test_duration", help="Duration of each steps", metavar="integer", default=10) +parser.add_option("-s", "--speed", dest="init_speed", help="Initial speed", metavar="integer", default=100) +parser.add_option("-u", "--use-case", dest="use_case", help="Use Case Number", metavar="integer", default=0) +parser.add_option("-r", "--run", dest="run", help="Run test", metavar="integer", default=0) +parser.add_option("-c", "--configure", dest="configure", help="Configure Test", metavar="integer", default=0) +(options, args) = parser.parse_args() + +init_speed = int(options.init_speed) +test_duration = int(options.test_duration) +use_case = int(options.use_case) +configure = int(options.configure) +run = int(options.run) + +nb_cores_per_interface = len(tx_port0) +max_speed = (100.0/nb_cores_per_interface) +init_speed = (init_speed * 1.0/nb_cores_per_interface) +accuracy = (accuracy * 1.0/nb_cores_per_interface) +normal_steps_delta_for_loss = (normal_steps_delta_for_loss /nb_cores_per_interface) +medium_steps_delta_for_loss = (medium_steps_delta_for_loss /nb_cores_per_interface) +low_steps_delta_for_loss = (low_steps_delta_for_loss /nb_cores_per_interface) +medium_steps = (medium_steps /nb_cores_per_interface) +low_steps = (low_steps /nb_cores_per_interface) + +max_dropped = max_dropped / 100 + +def to_str(arr): + ret = "" + first = 1; + for a in arr: + if (first == 0): + ret += "," + + ret += str(a) + first = 0; + return ret; + +tx_cores = tx_port0 + tx_port1 + tx_port2 + tx_port3 + tx_port4 + tx_port5 + tx_port6 + tx_port7 + +def send_all_pkt_size(cores, pkt_size): + for c in cores: + sock.sendall("pkt_size " + str(c) + " 0 " + str(pkt_size) + "\n"); + +def send_all_value(cores, offset, value, len): + for c in cores: + sock.sendall("set value " + str(c) + " 0 " + str(offset) + " " + str(value) + " " + str(len)+ "\n"); + +def send_all_random(cores, offset, rand_str, len): + for c in cores: + sock.sendall("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + #print("set random " + str(c) + " 0 " + str(offset) + " " + str(rand_str) + " " + str(len)+ "\n"); + +def send_all_speed(cores, speed_perc): + for c in cores: + sock.sendall("speed " + str(c) + " 0 " + str(speed_perc) + "\n"); + +def send_reset_random(): + sock.sendall("reset randoms all" + "\n"); + +def send_reset_value(): + sock.sendall("reset values all" + "\n"); + +def rx_stats(tx_cores, tx_task, rx_cores, rx_task): + rx = tx = drop = tsc = tsc_hs = ierrors = 0 + for e in tx_cores: + sock.sendall("core stats " + str(e) + " " + str(tx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + for e in rx_cores: + sock.sendall("core stats " + str(e) + " " + str(rx_task) + "\n") + recv = recv_once() + rx += int(recv.split(",")[0]) + tx += int(recv.split(",")[1]) + drop += int(recv.split(",")[2]) + tsc = int(recv.split(",")[3]) + tsc_hz = int(recv.split(",")[4]) + # Also get the ierrors as generators might be the bottleneck... + sock.sendall("tot ierrors tot\n") + recv = recv_once() + ierrors += int(recv.split(",")[0]) + rx+=ierrors + return rx,tx,drop,tsc,tsc_hz + +def lat_stats(cores,task): + lat_min = [0 for e in range(127)] + lat_max = [0 for e in range(127)] + lat_avg = [0 for e in range(127)] + for e in cores: + sock.sendall("lat stats " + str(e) + " " + str(task) + " " + "\n") + recv = recv_once() + lat_min[e] = int(recv.split(",")[0]) + lat_max[e] = int(recv.split(",")[1]) + lat_avg[e] = int(recv.split(",")[2]) + return lat_min, lat_max, lat_avg + +def recv_once(): + ret_str = ""; + done = 0; + while done == 0: + dat = sock.recv(256); + i = 0; + while(i < len(dat)): + if (dat[i] == '\n'): + done = 1 + else: + ret_str += dat[i]; + i = i + 1; + return ret_str + +def wait_vRouter_restarted(host): + while (1): + ret = os.system("ping " + host + " -c 1 > /dev/null") + if ret == 0: + print "still up..." + else: + break; + sleep(1) + + while (1): + ret = os.system("ping " + host + " -c 1 > /dev/null") + if (ret == 0): + print "UP" + break; + else: + print "still down..." + sleep(1) + +def reload_vRouter_config(config): + print "connecting to vRouter...and copying " + str(config) + sut = remote_system("root", vRouter_host) + cmd = "cp /config/prox/" + str(config) + " /config/config.boot" + sut.run(cmd) + print "Rebooting system at " + str(datetime.now().time()) + sut.run_forked("reboot") + sleep(5) + wait_vRouter_restarted(vRouter_host) + print "Waiting for last startup scripts to start..." + last_script = "l2tp" + while(1): + dmesg = str(sut.run("dmesg")) + if last_script in dmesg: + print "found l2tp - UP" + break; + sleep(1) + print "vRouter started - waiting 5 last seconds before starting test" + sleep(5) + print datetime.now().time() + +def set_pkt_sizes(tx_cores, p): + send_all_pkt_size(tx_cores, p-4) + # For all cores, need to adapt IP Length (byte 16) and UDP Length (byte 38) to pkt size + send_all_value(tx_cores, 16, p - 18, 2) # 14 for MAC (12) EthType (2) + send_all_value(tx_cores, 38, p - 38, 2) # 34 for MAC (12) EthType (2) IP (20) + +def run_measure_throughput(speed): + done = 0 + # Intialize tests by stopping cores and resetting stats + step=0 + steps_done = 0 + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(2) + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + sock.sendall("reset stats\n") + print "Speed = " + str(speed * nb_cores_per_interface) + sleep(1); + + send_all_speed(tx_cores, step); + + # Now starting the steps. First go to the common speed, then increase steps for the faster one. + sock.sendall("start " + to_str(tx_cores) + "," + to_str(rx_lat_cores) + "\n") + while (steps_done == 0): + sleep(step_time) + if (step + step_delta <= speed): + step+=step_delta + else: + steps_done = 1; + send_all_speed(tx_cores, step) + + # Steps are now OK. Set speed + send_all_speed(tx_cores, speed); + sleep(2); + + # Getting statistics to calculate PPS at right speed.... + rx_pps_beg,tx_pps_beg,drop_pps_beg,tsc_pps_beg,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + sleep(test_duration); + + # Collect statistics before test stops...and stop the test. Important to get stats before stopping as stops take some time... + rx_pps_end,tx_pps_end,drop_pps_end,tsc_pps_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + lat_min,lat_max,lat_avg = lat_stats(rx_lat_cores, rx_task) + sock.sendall("stop " + "," + to_str(tx_cores) + "\n") + sock.sendall("start " + to_str(all_rx_cores) + "\n") + sleep(3); + sock.sendall("stop " + to_str(all_rx_cores) + "\n") + + rx_end, tx_end,drop_end,tsc_end,tsc_hz = rx_stats(tx_cores, tx_task, all_rx_cores, rx_task); + rx = rx_pps_end - rx_pps_beg + tsc = tsc_pps_end - tsc_pps_beg + mpps = rx / (tsc/float(tsc_hz)) / 1000000 + tx = tx_pps_end - tx_pps_beg + tx_mpps = tx / (tsc/float(tsc_hz)) / 1000000 + + #print "Runtime = " + str((tsc)/float(tsc_hz)); + if (tx_end == 0): + dropped_tot = tx_end - rx_end + dropped_pct = 0 + else: + dropped_tot = tx_end - rx_end + dropped_pct = ((dropped_tot) * 1.0) / tx_end + + if (dropped_tot > 0): + if (dropped_pct >= max_dropped): + print "** FAILED **: lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + print "OK but lost " + str(100*dropped_pct) + "% packets RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + else: + if (dropped_tot < 0): + print "Something wrong happened - received more packets than transmitted" + else: + print "** OK **: RX = " + str(rx_end) + " TX = " + str(tx_end) + " DROPPED = " + str(tx_end - rx_end) + print "MPPS = " + str(mpps) + print "====================================================" + return dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg + +def write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg): + f.write(str(pkt_size) + "; " + str(tx_mpps) + "; " + str(mpps) + "; " + str(100 * dropped_pct) + "; " + str(dropped_tot) + "; " + str(speed * nb_cores_per_interface) + "; " + str(number_next_hops) + "; " + str(number_routes) + "; " + str(traffic) + "; ") + for e in rx_lat_cores: + f.write(str(lat_min[e]) + "; " + str(lat_max[e]) + "; " + str(lat_avg[e]) + "; ") + f.write("\n"); + f.flush() + +def run_loss_graph(number_next_hops, number_routes, pkt_size, traffic): + speed = init_speed * 1.0 + done = 0; + while done == 0: + dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg = run_measure_throughput(speed) + write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + if (speed <= low_steps_delta_for_loss): + done = 1 + return + if (speed >= (medium_steps+normal_steps_delta_for_loss)): + speed -= normal_steps_delta_for_loss + else: + if (speed >= (low_steps+medium_steps_delta_for_loss)): + speed -= medium_steps_delta_for_loss + else: + speed -= low_steps_delta_for_loss + +def run_dicho_search(number_next_hops, number_routes, pkt_size, traffic): + previous_success_speed = 0.0 + previous_error_speed = max_speed + speed = init_speed * 1.0 + done = 0; + good_tx_mpps = 0 + good_mpps = 0 + good_dropped_pct = 0 + good_dropped_tot = 0 + good_speed = 0 + good_lat_min = [0 for e in range(127)] + good_lat_max = [0 for e in range(127)] + good_lat_avg = [0 for e in range(127)] + + while done == 0: + dropped_pct, mpps, tx_mpps, dropped_tot,lat_min,lat_max,lat_avg = run_measure_throughput(speed) + if ((dropped_tot >= 0) and (dropped_pct <= max_dropped)): + good_tx_mpps = tx_mpps + good_mpps = mpps + good_dropped_pct = dropped_pct + good_dropped_tot = dropped_tot + good_speed = speed + good_lat_min = lat_min + good_lat_max = lat_max + good_lat_avg = lat_avg + write_results(f, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + else: + write_results(f_all, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + + if ((speed == max_speed) and (dropped_pct <= max_dropped)): + write_results(f_minimal, pkt_size, tx_mpps, mpps, dropped_pct, dropped_tot, speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, lat_min, lat_max, lat_avg); + done = 1 + if (dropped_pct <= max_dropped): + previous_success_speed = speed + if (speed > max_speed - accuracy): + speed = max_speed + else: + if (previous_error_speed - speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed + (previous_error_speed - speed)/2; + else: + previous_error_speed = speed + if (speed - previous_success_speed < accuracy): + write_results(f_minimal, pkt_size, good_tx_mpps, good_mpps, good_dropped_pct, good_dropped_tot, good_speed, nb_cores_per_interface, number_next_hops, number_routes, traffic, good_lat_min, good_lat_max, good_lat_avg); + done = 1 + else: + speed = speed - (speed - previous_success_speed) / 2; + + +def set_destination_ip(use_case, nb_destinations, traffic): + # minimmum 8 routes i.e. 1 per interface + # Destination addressese: "00XXXYY1" "Z00ZZ0ZZ" "AA0AA0AA" "BBBBBB10" + # Where X = interface id. Starting with 00 to be in class A and skipping 0.x.y.z and 127.x.y.z + # Y, Z and A = additional routes + # B = IP in routes. 10 to avoid x.y.z.0 and x.y.z.255 + # Gaps in A and B to void "too good" distributions e.g. using LPM and + # First changing Y + + mask = "" + for i in range (2): + mask = str(mask)+"0" + end_mask = "" + if (use_case != 2): + end_mask = "XXXXXX10" # Last 8 bits + + if (nb_destinations == 1): + end_mask = "0010000000000000000" + str(end_mask) + if (nb_destinations == 2): + end_mask = "X010000000000000000" + str(end_mask) + if (nb_destinations == 4): + end_mask = "XX10000000000000000" + str(end_mask) + if (nb_destinations == 8): + end_mask = "XX1X000000000000000" + str(end_mask) + elif (nb_destinations == 16): + end_mask = "XX1X00X000000000000" + str(end_mask) + elif (nb_destinations == 32): + end_mask = "XX1X00XX00000000000" + str(end_mask) + elif (nb_destinations == 64): + end_mask = "XX1X00XX0X000000000" + str(end_mask) + elif (nb_destinations == 128): + end_mask = "XX1X00XX0XX00000000" + str(end_mask) + elif (nb_destinations == 256): + end_mask = "XX1X00XX0XXX0000000" + str(end_mask) + elif (nb_destinations == 512): + end_mask = "XX1X00XX0XXXX000000" + str(end_mask) + elif (nb_destinations == 1024): + end_mask = "XX1X00XX0XXXX0X0000" + str(end_mask) + elif (nb_destinations == 2048): + end_mask = "XX1X00XX0XXXX0XX000" + str(end_mask) + elif (nb_destinations == 4096): + end_mask = "XX1X00XX0XXXX0XX0X0" + str(end_mask) + elif (nb_destinations == 8192): + end_mask = "XX1X00XX0XXXX0XX0XX" + str(end_mask) + else: + if (nb_destinations <= 64 * 1): + end_mask = "0010000000000000000" + n_dest = int(log(nb_destinations, 2)) + for i in range (n_dest): + end_mask = str(end_mask) + "X" + for i in range (6 - n_dest): + end_mask = str(end_mask) + "0" + end_mask = str(end_mask) + "10" + else: + end_mask = "XXXXXX10" # Last 8 bits + + if (nb_destinations == 64 * 2): + end_mask = "001X000000000000000" + str(end_mask) + elif (nb_destinations == 64 * 4): + end_mask = "001X00X000000000000" + str(end_mask) + elif (nb_destinations == 64 * 8): + end_mask = "001X00XX00000000000" + str(end_mask) + elif (nb_destinations == 64 * 16): + end_mask = "001X00XX0X000000000" + str(end_mask) + elif (nb_destinations == 64 * 32): + end_mask = "001X00XX0XX00000000" + str(end_mask) + elif (nb_destinations == 64 * 64): + end_mask = "001X00XX0XXX0000000" + str(end_mask) + elif (nb_destinations == 64 * 128): + end_mask = "001X00XX0XXXX000000" + str(end_mask) + elif (nb_destinations == 64 * 256): + end_mask = "001X00XX0XXXX0X0000" + str(end_mask) + elif (nb_destinations == 64 * 512): + end_mask = "001X00XX0XXXX0XX000" + str(end_mask) + elif (nb_destinations == 64 * 1024): + end_mask = "001X00XX0XXXX0XX0X0" + str(end_mask) + elif (nb_destinations == 64 * 2048): + end_mask = "001X00XX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 4096): + end_mask = "001XX0XX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 8192): + end_mask = "001XXXXX0XXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 16384): + end_mask = "001XXXXXXXXXX0XX0XX" + str(end_mask) + elif (nb_destinations == 64 * 32768): + end_mask = "001XXXXXXXXXXXXX0XX" + str(end_mask) + elif (nb_destinations == 64 * 65536): + end_mask = "001XXXXXXXXXXXXXXXX" + str(end_mask) + + if (traffic == 0): # One-to-one. From odd interface to even interface and vice versa, no QPI cross + mask1 = str(mask) + "001" + str(end_mask) + mask2 = str(mask) + "000" + str(end_mask) + mask3 = str(mask) + "011" + str(end_mask) + mask4 = str(mask) + "010" + str(end_mask) + mask5 = str(mask) + "101" + str(end_mask) + mask6 = str(mask) + "100" + str(end_mask) + mask7 = str(mask) + "111" + str(end_mask) + mask8 = str(mask) + "110" + str(end_mask) + + elif (traffic == 1): # Full mesh within QPI (i.e. 1 to 4) + mask1 = str(mask) + "0XX" + str(end_mask) + mask2 = str(mask) + "0XX" + str(end_mask) + mask3 = str(mask) + "0XX" + str(end_mask) + mask4 = str(mask) + "0XX" + str(end_mask) + mask5 = str(mask) + "1XX" + str(end_mask) + mask6 = str(mask) + "1XX" + str(end_mask) + mask7 = str(mask) + "1XX" + str(end_mask) + mask8 = str(mask) + "1XX" + str(end_mask) + + elif (traffic == 2): # One to one, crossing QPI (100% QPI) + mask1 = str(mask) + "100" + str(end_mask) + mask2 = str(mask) + "101" + str(end_mask) + mask3 = str(mask) + "110" + str(end_mask) + mask4 = str(mask) + "111" + str(end_mask) + mask5 = str(mask) + "000" + str(end_mask) + mask6 = str(mask) + "001" + str(end_mask) + mask7 = str(mask) + "010" + str(end_mask) + mask8 = str(mask) + "011" + str(end_mask) + + elif (traffic == 3): # 1 to 4 crossing QPI (100% QPI) + mask1 = str(mask) + "1XX" + str(end_mask) + mask2 = str(mask) + "1XX" + str(end_mask) + mask3 = str(mask) + "1XX" + str(end_mask) + mask4 = str(mask) + "1XX" + str(end_mask) + mask5 = str(mask) + "0XX" + str(end_mask) + mask6 = str(mask) + "0XX" + str(end_mask) + mask7 = str(mask) + "0XX" + str(end_mask) + mask8 = str(mask) + "0XX" + str(end_mask) + + elif (traffic == 4): # 1 to 4 (50% QPI) + mask1 = str(mask) + "XX1" + str(end_mask) + mask2 = str(mask) + "XX0" + str(end_mask) + mask3 = str(mask) + "XX1" + str(end_mask) + mask4 = str(mask) + "XX0" + str(end_mask) + mask5 = str(mask) + "XX1" + str(end_mask) + mask6 = str(mask) + "XX0" + str(end_mask) + mask7 = str(mask) + "XX1" + str(end_mask) + mask8 = str(mask) + "XX0" + str(end_mask) + + elif (traffic == 5): # Full mesh (50% QPI) + mask1 = str(mask) + "XXX" + str(end_mask) + mask2 = str(mask) + "XXX" + str(end_mask) + mask3 = str(mask) + "XXX" + str(end_mask) + mask4 = str(mask) + "XXX" + str(end_mask) + mask5 = str(mask) + "XXX" + str(end_mask) + mask6 = str(mask) + "XXX" + str(end_mask) + mask7 = str(mask) + "XXX" + str(end_mask) + mask8 = str(mask) + "XXX" + str(end_mask) + + for c in tx_port0: + send_all_random([c], 30, mask1, 4) + for c in tx_port1: + send_all_random([c], 30, mask2, 4) + for c in tx_port2: + send_all_random([c], 30, mask3, 4) + for c in tx_port3: + send_all_random([c], 30, mask4, 4) + for c in tx_port4: + send_all_random([c], 30, mask5, 4) + for c in tx_port5: + send_all_random([c], 30, mask6, 4) + for c in tx_port6: + send_all_random([c], 30, mask7, 4) + for c in tx_port7: + send_all_random([c], 30, mask8, 4) + for c in tx_cores: + send_all_random([c], 34, "0XXXXXXXXXXXXX10", 2) + send_all_random([c], 36, "0XXXXXXXXXXXXX10", 2) + +#======================================================================== +class TestDefinition(): + "Stores test parameters" + def __init__(self, use_case, next_hops, number_routes, pkt_size, traffic, reload): + self.use_case = use_case + self.next_hops = next_hops + self.number_routes = number_routes + self.pkt_size = pkt_size + self.traffic = traffic + self.reload = reload + +#======================================================================== +# Use case 0 increases input load and measure output load => show dropped packets at low loads, show overload behavior +# Use case 1 and use case 2 run dichotomic searches, searching for 0 packet loss (or whaever loss is configured) +# Use case 1 shows the effect of number of routes and next-hops +# Use case 2 shows the effect of the number of destination, using a fixed (low) number of routes and next-hops +#======================================================================== +def run_use_case(use_case, number_next_hops, number_routes, pkt_size, traffic, reload): + if (reload): + if (use_case == 2): + config = "config.1_1" + "_" + str(use_case) + ".boot" + else: + config = "config." + str(number_routes) + "_" + str(number_next_hops) + ".boot" + reload_vRouter_config(config) + send_reset_random() + send_reset_value() + set_destination_ip(use_case, number_routes, traffic) + set_pkt_sizes(tx_cores, pkt_size) + print "Running test with pkt size= " + str(pkt_size) + " Next hops = " + str(number_next_hops) + "; number of routes = " + str(number_routes) + "; Traffic = " + str(traffic) + " \n" + if (use_case == 0): + run_loss_graph(number_next_hops, number_routes, pkt_size, traffic) + else: + run_dicho_search(number_next_hops, number_routes, pkt_size, traffic) + sleep(3) + +#======================================================================== +def run_all_use_cases(): + use_case_nb = 1 + # Connect to dppd + file_path = '/tmp/prox.sock' + sock.connect(file_path) + + f.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_all.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f_minimal.write("pkt_size; tx_mpps; rx_mpps; dropped_pct; dropped_tot; percent_line_rate; latency per core\n") + f.flush(); + f_all.flush(); + f_minimal.flush(); + + # Starting tests + print "Stopping all cores and resetting all values and randoms before starting\n" + sock.sendall("stop all") + sock.sendall("reset stats\n") + sleep(3); + for line in file_tests: + info = line.split(';') + if (info[0][0] == '#'): + continue + if (info[0][0] == ''): + break + use_case = int(info[0]) + next_hops = int(info[1]) + number_routes = int(info[2]) + pkt_size = int(info[3]) + traffic = int(info[4]) + reload = int(info[5]) + print str(use_case_nb) + " : Running use case " + str(use_case) + " next_hops = " + str(next_hops) + " routes = " + str(number_routes) + " pkt_size = " + str(pkt_size) + " traffic = " + str(traffic) + " reload = " + str(reload) + run_use_case(use_case, next_hops, number_routes, pkt_size, traffic, reload) + use_case_nb = use_case_nb + 1 + +#======================================================================== +def configure_use_case(use_case): + Tests = [] + if (use_case == 0): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition("0", "1", "1", pkt_size, "0", "1")) + for pkt_size in all_pkt_size: + Tests.append(TestDefinition("0", "1", "1", pkt_size, "1", "1")) + if (use_case == 1): + number_next_hops = 1 + reload = 0 + + number_routes = number_next_hops # At least same number of routes that number of next hops + while number_routes <= max_number_routes: + reload = 1 + for traffic in range(6): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, number_routes, pkt_size, traffic, reload)) + reload = 0 + if (number_routes < max_number_routes / 2): + number_routes = number_routes * 4 + else: + number_routes = number_routes * 2 + + number_routes = max_number_next_hops + while number_next_hops <= max_number_next_hops: + reload = 1 + for traffic in range(6): + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, number_routes, pkt_size, traffic, reload)) + reload = 0 + number_next_hops = number_next_hops * 2 + if (use_case == 2): + number_next_hops = 1 + reload = 1 + for traffic in range(6): + nb_destinations = 1 + while nb_destinations <= max_number_addresses_local_network: + for pkt_size in all_pkt_size: + Tests.append(TestDefinition(use_case, number_next_hops, nb_destinations, pkt_size, traffic, reload)) + reload = 0 + nb_destinations = nb_destinations * 2 + reload = 1 + + file_tests = open('test_description.txt', 'w') + file_tests.write("# Use case; next_hops; routes; pkt_size; traffic; reload;\n") + for test in Tests: + file_tests.write(str(test.use_case) + "; " + str(test.next_hops) + "; " + str(test.number_routes) + "; " + str(test.pkt_size) + "; " + str(test.traffic) + "; " + str(test.reload) + ";\n") + file_tests.close() + +#======================================================================== +if ((configure == 0) and (run == 0)): + print "Nothing to do - please use -r 1 or -c 1" +if (configure == 1): + configure_use_case(use_case) +if (run == 1): + print "****************************************************************************************************************" + print "** Running vRouter Characterization with " + str(test_duration) + " seconds steps and starting at " + str(init_speed) + " percent of line rate **" + print "****************************************************************************************************************" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + f_all = open('all_results.txt', 'w') + f = open('detailed_results.txt', 'w') + f_minimal = open('minimal_results.txt', 'w') + file_tests = open('test_description.txt', 'r') + run_all_use_cases() + f.close(); + sock.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/testvRouter/create_interfaces_and_routes.pl b/VNFs/DPPD-PROX/helper-scripts/testvRouter/create_interfaces_and_routes.pl new file mode 100755 index 00000000..b8baa46b --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/testvRouter/create_interfaces_and_routes.pl @@ -0,0 +1,90 @@ +#!/bin/env perl + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +# This script creates four sets of files: 2 sets for use case 0 and 1 +# (which use the same configuration) and 2 for use case 2. +# Each use case is defined by 2 sets of configuration files. +# interface.txt contains the IP addresses of the DPDK fast path interfaces. +# route.x.y.txt contains the routing table for different configurations +# with x being number of routes and y number of next_hops. +# Those interface.txt and route.x.y.txt files should then be converted +# to fit the syntax of vRouter configuration files. + +use strict; +my $max_nb_routes = 8192; +my $max_nb_next_hops = 1024; +my $max_nb_interfaces = 4; +my $nb_next_hops = 1; +my ($interface, $a1, $a2, $a3, $a4, $fh, $output_route); + +# Create interface configuration for use case 0 and 1 +my $interface_config = "interface.txt"; +open($fh, '>', $interface_config) or die "Could not open file '$interface_config' $!"; +print $fh "# interface IP address/prefix\n"; +for ($interface = 0; $interface < $max_nb_interfaces; $interface++) { + print $fh ($interface+64).".0.0.240/24\n"; +} +close $fh; + +# Create interface configuration for use case 2 +my $interface_config = "interface_use_case_2.txt"; +open($fh, '>', $interface_config) or die "Could not open file '$interface_config' $!"; +print $fh "# interface IP address/prefix\n"; +for ($interface = 0; $interface < $max_nb_interfaces; $interface++) { + print $fh ($interface * 8 + 1).".0.0.240/5\n"; +} +close $fh; + +# Create routes configuration for use case 0 and 1 +while ($nb_next_hops <= $max_nb_next_hops) { + my $nb_routes_per_interface = $nb_next_hops; + while ($nb_routes_per_interface <= $max_nb_routes) { + $output_route = "route.".$nb_routes_per_interface.".".$nb_next_hops.".txt"; + open($fh, '>', $output_route) or die "Could not open file '$output_route' $!"; + print $fh "# destination/prefix;nex-hop\n"; + + for (my $route_nb = 0; $route_nb < $nb_routes_per_interface; $route_nb++) { + for ($interface = 0; $interface < $max_nb_interfaces; $interface++) { + $a1 = $interface * 8 + 1 + (($route_nb & 1) << 2) + ($route_nb & 2); + $a2 = (($route_nb & 4) << 5) + (($route_nb & 8) << 1) + (($route_nb & 0x10) >> 1) + (($route_nb & 0x20) >> 4) + (($route_nb & 0x40) >> 6); + $a3 = (($route_nb & 0x80)) + (($route_nb & 0x100) >> 2) + (($route_nb & 0x200) >> 5) + (($route_nb & 0x400) >> 7) + (($route_nb & 0x800) >> 10) + (($route_nb & 0x1000) >> 12); + $a4 = 0; + print $fh $a1.".".$a2.".".$a3.".".$a4."/24;"; + print $fh ($interface+64).".0.".(($route_nb % $nb_next_hops) >> 7).".".(1 + (($route_nb % $nb_next_hops) & 0x7f)) ."\n"; + } + } + $nb_routes_per_interface = $nb_routes_per_interface * 2; + } + $nb_next_hops = $nb_next_hops * 2; +} +close $fh; + +# Create routes configuration for use case 2 +$output_route = "route.1.1.use_case_2.txt"; +open($fh, '>', $output_route) or die "Could not open file '$output_route' $!"; +print $fh "# destination/prefix;nex-hop\n"; + +for ($interface = 0; $interface < $max_nb_interfaces; $interface++) { + $a1 = $interface + 64 ; + $a2 = 0; + $a3 = 0; + $a4 = 0; + print $fh $a1.".".$a2.".".$a3.".".$a4."/24;"; + print $fh ($interface * 8 + 1).".0.0.1\n"; +} +close $fh; diff --git a/VNFs/DPPD-PROX/helper-scripts/testvRouter/remote_system.py b/VNFs/DPPD-PROX/helper-scripts/testvRouter/remote_system.py new file mode 100755 index 00000000..f00ab77b --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/testvRouter/remote_system.py @@ -0,0 +1,57 @@ +#!/bin/env python + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +import os +import thread +import time +import socket + +def ssh(user, ip, cmd): + # print cmd; + ssh_options = "" + ssh_options += "-o StrictHostKeyChecking=no " + ssh_options += "-o UserKnownHostsFile=/dev/null " + ssh_options += "-o LogLevel=quiet " + running = os.popen("ssh " + ssh_options + " " + user + "@" + ip + " \"" + cmd + "\""); + ret = {}; + ret['out'] = running.read().strip(); + ret['ret'] = running.close(); + if (ret['ret'] == None): + ret['ret'] = 0; + + return ret; + +def ssh_check_quit(obj, user, ip, cmd): + ret = ssh(user, ip, cmd); + if (ret['ret'] != 0): + obj._err = True; + obj._err_str = ret['out']; + exit(-1); + +class remote_system: + def __init__(self, user, ip): + self._ip = ip; + self._user = user; + def run(self, cmd): + return ssh(self._user, self._ip, cmd); + def run_forked(self, cmd): + thread.start_new_thread(ssh, (self._user, self._ip, cmd)); + return 0; + def scp(self, src, dst): + running = os.popen("scp " + self._user + "@" + self._ip + ":" + src + " " + dst); + return running.close(); diff --git a/VNFs/DPPD-PROX/helper-scripts/trailing.sh b/VNFs/DPPD-PROX/helper-scripts/trailing.sh new file mode 100755 index 00000000..5b64b1d7 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/trailing.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +bad_lines=$(grep -nHr -e "[[:space:]]$" *.c *.h gen/*.cfg config/*.cfg) + +if [ -n "$bad_lines" ]; then + echo "Found trailing white-spaces:" + echo $bad_lines + exit 1; +fi + +for f in *.c *.h gen/*.cfg config/*.cfg; do + result=$(tail -n 1 $f | grep "^$" | wc -l) + + if [ "$result" == "1" ]; then + echo "Trailing newlines at end of file $f" + exit 1 + fi +done; + +prev="dummy" +function findDuplicate() { + line=1 + while read p; do + if [ "$prev" == "" ]; then + if [ "$p" == "" ]; then + echo "duplicate empty line at $1:$line" + bad=1 + fi + fi + prev=$p + let "line+=1" + done <$1 +} + +bad=0 +for f in *.c *.h; do + findDuplicate $f +done; + +if [ "$bad" != "0" ]; then + exit 1 +fi + +tab=" " +bad_lines=$(grep -nHr -e "^$tab$tab$tab$tab$tab$tab$tab" *.c *.h | head -n1) + +if [ -n "$bad_lines" ]; then + echo "Code nested too deep:" + echo $bad_lines + exit 1; +fi + +exit 0 diff --git a/VNFs/DPPD-PROX/helper-scripts/vm-cores.py b/VNFs/DPPD-PROX/helper-scripts/vm-cores.py new file mode 100644 index 00000000..de794998 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/vm-cores.py @@ -0,0 +1,20 @@ +#!/bin/env python2.7 + +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +cores = [[0,20], [1,21], [2,22], [3,23], [4,24], [5,25], [6,26], [7,27], [8,28], [9,29]] + diff --git a/VNFs/DPPD-PROX/input.c b/VNFs/DPPD-PROX/input.c new file mode 100644 index 00000000..bb956bcd --- /dev/null +++ b/VNFs/DPPD-PROX/input.c @@ -0,0 +1,105 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_common.h> + +#include "clock.h" +#include "input.h" + +static struct input *inputs[32]; +static int n_inputs; +static int max_input_fd; + +int reg_input(struct input *in) +{ + if (n_inputs == sizeof(inputs)/sizeof(inputs[0])) + return -1; + + for (int i = 0; i < n_inputs; ++i) { + if (inputs[i] == in) + return -1; + } + inputs[n_inputs++] = in; + max_input_fd = RTE_MAX(in->fd, max_input_fd); + + return 0; +} + +void unreg_input(struct input *in) +{ + int rm, i; + + for (rm = 0; rm < n_inputs; ++rm) { + if (inputs[rm] == in) { + break; + } + } + + if (rm == n_inputs) + return ; + + for (i = rm + 1; i < n_inputs; ++i) { + inputs[i - 1] = inputs[i]; + } + + n_inputs--; + max_input_fd = 0; + for (i = 0; i < n_inputs; ++i) { + max_input_fd = RTE_MAX(inputs[i]->fd, max_input_fd); + } +} + +static int tsc_diff_to_tv(uint64_t beg, uint64_t end, struct timeval *tv) +{ + if (end < beg) { + return -1; + } + + uint64_t diff = end - beg; + tsc_to_tv(tv, diff); + return 0; +} + +void input_proc_until(uint64_t deadline) +{ + struct timeval tv; + fd_set in_fd; + int ret = 1; + + /* Keep checking for input until select() returned 0 (timeout + occurred before input was read) or current time has passed + the deadline (which occurs when time progresses past the + deadline between return of select() and the next + iteration). */ + while (ret != 0 && tsc_diff_to_tv(rte_rdtsc(), deadline, &tv) == 0) { + FD_ZERO(&in_fd); + + for (int i = 0; i < n_inputs; ++i) { + FD_SET(inputs[i]->fd, &in_fd); + } + + ret = select(max_input_fd + 1, &in_fd, NULL, NULL, &tv); + + if (ret > 0) { + for (int i = 0; i < n_inputs; ++i) { + if (FD_ISSET(inputs[i]->fd, &in_fd)) { + inputs[i]->proc_input(inputs[i]); + } + } + } + } +} diff --git a/VNFs/DPPD-PROX/input.h b/VNFs/DPPD-PROX/input.h new file mode 100644 index 00000000..06f6b653 --- /dev/null +++ b/VNFs/DPPD-PROX/input.h @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _INPUT_H_ +#define _INPUT_H_ + +#include <inttypes.h> + +struct input { + int fd; + /* Function to be called when data is available on the fd */ + void (*proc_input)(struct input *input); + void (*reply)(struct input *input, const char *buf, size_t len); + void (*history)(struct input *input); +}; + +int reg_input(struct input *in); +void unreg_input(struct input *in); + +void input_proc_until(uint64_t deadline); + +#endif /* _INPUT_H_ */ diff --git a/VNFs/DPPD-PROX/input_conn.c b/VNFs/DPPD-PROX/input_conn.c new file mode 100644 index 00000000..63e6511e --- /dev/null +++ b/VNFs/DPPD-PROX/input_conn.c @@ -0,0 +1,236 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include "input_conn.h" +#include "input.h" +#include "run.h" +#include "cmd_parser.h" + +static struct input tcp_server; +int tcp_server_started; +static struct input uds_server; +int uds_server_started; + +/* Active clients */ +struct client_conn { + struct input input; + int enabled; + int n_buf; + char buf[32768]; +}; + +struct client_conn clients[32]; + +static int start_listen_tcp(void) +{ + struct sockaddr_in server; + int ret, sock; + int optval = 1; + + memset(&server, 0, sizeof(server)); + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock == -1) + return -1; + + server.sin_family = AF_INET; + server.sin_port = ntohs(8474); + server.sin_addr.s_addr = ntohl(INADDR_ANY); + + ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)); + + if (ret) + return -1; + + if (bind(sock, (struct sockaddr *) &server, sizeof(server)) == -1) + return -1; + + if (listen(sock, 1) == -1) + return -1; + + return sock; +} + +static int start_listen_uds(void) +{ + int sock; + struct sockaddr_un server = { + .sun_path = "/tmp/prox.sock", + .sun_family = AF_UNIX + }; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) + return -1; + + /* Unlink can fail, i.e. when /tmp/prox.sock does not + exists. This is not fatal. */ + unlink(server.sun_path); + + if (bind(sock, (struct sockaddr *) &server, sizeof(server)) == -1) + return -1; + + if (listen(sock, 1) == -1) + return -1; + + return sock; +} + +static void write_client(struct input *input, const char *buf, size_t len) +{ + int ret; + + while ((ret = write(input->fd, buf, len)) != (int)len) { + buf += ret; + len -= ret; + } +} + +static void handle_client(struct input* client_input) +{ + char cur[1024]; + size_t i; + int ret; + struct client_conn *c = NULL; + + /* Get the client structure that uses this input */ + for (i = 0; i < sizeof(clients)/sizeof(clients[0]); ++i) { + if (&clients[i].input == client_input) { + c = &clients[i]; + break; + } + } + + /* handle_client function called non-tcp client */ + if (c == NULL) + return ; + + ret = read(c->input.fd, cur, sizeof(cur)); + + if (ret == 0) { + c->enabled = 0; + unreg_input(&c->input); + return ; + } + + /* Scan in data until \n (\r skipped if followed by \n) */ + for (int i = 0; i < ret; ++i) { + if (cur[i] == '\r' && i + 1 < ret && cur[i + 1] == '\n') + continue; + + if (cur[i] == '\n') { + c->buf[c->n_buf] = 0; + if (c->n_buf) + cmd_parser_parse(c->buf, client_input); + c->n_buf = 0; + } + else if (c->n_buf + 1 < (int)sizeof(c->buf)) + c->buf[c->n_buf++] = cur[i]; + else + c->n_buf = 0; + } +} + +static void handle_new_client(struct input* server) +{ + size_t i; + + int new_client = accept(server->fd, NULL, NULL); + + for (i = 0; i < sizeof(clients)/sizeof(clients[0]); ++i) { + if (clients[i].enabled == 0) { + break; + } + } + + if (i == sizeof(clients)/sizeof(clients[0])) { + close(new_client); + return ; + } + + clients[i].enabled = 1; + clients[i].n_buf = 0; + clients[i].input.fd = new_client; + clients[i].input.reply = server->reply; + clients[i].input.proc_input = handle_client; + + reg_input(&clients[i].input); +} + +int reg_input_tcp(void) +{ + int fd; + + if (tcp_server_started) + return -1; + if ((fd = start_listen_tcp()) < 0) + return -1; + + tcp_server.fd = fd; + tcp_server.proc_input = handle_new_client; + tcp_server.reply = write_client; + if (reg_input(&tcp_server) != 0) { + close(fd); + return -1; + } + tcp_server_started = 1; + return 0; +} + +int reg_input_uds(void) +{ + int fd; + + if (uds_server_started) + return -1; + + if ((fd = start_listen_uds()) < 0) + return -1; + + uds_server.fd = fd; + uds_server.proc_input = handle_new_client; + uds_server.reply = write_client; + if (reg_input(&uds_server) != 0) { + close(fd); + return -1; + } + uds_server_started = 1; + return 0; +} + +void unreg_input_tcp(void) +{ + if (!tcp_server_started) + return; + tcp_server_started = 0; + close(tcp_server.fd); + unreg_input(&tcp_server); +} + +void unreg_input_uds(void) +{ + if (!uds_server_started) + return; + uds_server_started = 0; + close(tcp_server.fd); + unreg_input(&tcp_server); +} diff --git a/VNFs/DPPD-PROX/input_conn.h b/VNFs/DPPD-PROX/input_conn.h new file mode 100644 index 00000000..98e9af45 --- /dev/null +++ b/VNFs/DPPD-PROX/input_conn.h @@ -0,0 +1,27 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _INPUT_CONN_H_ +#define _INPUT_CONN_H_ + +/* Returns 0 on success, -1 otherwise. */ +int reg_input_tcp(void); +int reg_input_uds(void); + +void unreg_input_tcp(void); +void unreg_input_uds(void); + +#endif /* _INPUT_CONN_H_ */ diff --git a/VNFs/DPPD-PROX/input_curses.c b/VNFs/DPPD-PROX/input_curses.c new file mode 100644 index 00000000..6f79869b --- /dev/null +++ b/VNFs/DPPD-PROX/input_curses.c @@ -0,0 +1,325 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +#include "log.h" +#include "input.h" +#include "display.h" +#include "run.h" +#include "cmd_parser.h" +#include "input_curses.h" +#include "histedit.h" + +static EditLine *el; +static History *hist; + +static struct input input_curses; +static int tabbed; + +static void show_history(struct input *input) +{ + HistEvent event; + + history(hist, &event, H_LAST); + + do { + plog_info("%s", event.str); /* event.str contains newline */ + } while (history(hist, &event, H_PREV) != -1); +} + +static int complete(__attribute__((unused)) int ch) +{ + const LineInfo *li; + size_t len; + size_t n_match = 0; + char complete_cmd[128] = {0}; + int complete_cmd_partial = 0; + + li = el_line(el); + for (size_t i = 0; i < cmd_parser_n_cmd(); ++i) { + len = li->lastchar - li->buffer; + if (strncmp(cmd_parser_cmd(i), li->buffer, len) == 0) { + if (n_match) { + size_t cur_len = strlen(complete_cmd); + for (size_t j = 0; j < cur_len; ++j) { + if (complete_cmd[j] != cmd_parser_cmd(i)[j]) { + complete_cmd[j] = 0; + complete_cmd_partial = 1; + break; + } + } + } + else { + strcpy(complete_cmd, cmd_parser_cmd(i)); + } + + n_match++; + } + } + + /* Complete only if there are more characters known than + currently entered. */ + if (n_match && len < strlen(complete_cmd)) { + el_deletestr(el, li->cursor - li->buffer); + el_insertstr(el, complete_cmd); + if (!complete_cmd_partial) + el_insertstr(el, " "); + + return CC_REDISPLAY; + } + else if (tabbed) { + int printed = 0; + for (size_t i = 0; i < cmd_parser_n_cmd(); ++i) { + len = li->lastchar - li->buffer; + if (strncmp(cmd_parser_cmd(i), li->buffer, len) == 0) { + plog_info("%-23s", cmd_parser_cmd(i)); + printed++; + } + if (printed == 4) { + printed = 0; + plog_info("\n"); + } + } + if (printed) + plog_info("\n"); + } + else { + tabbed = 1; + } + + return CC_REDISPLAY; +} + +/* Returns non-zero if stdin is readable */ +static int peek_stdin(void) +{ + int tmp; + fd_set in_fd; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 10000; + + FD_ZERO(&in_fd); + FD_SET(fileno(stdin), &in_fd); + tmp = select(fileno(stdin) + 1, &in_fd, NULL, NULL, &tv); + return FD_ISSET(fileno(stdin), &in_fd); +} + +static int get_char(EditLine *e, char *c) +{ + *c = display_getch(); + + /* If no characters have been entered, number keys switch the + screen and '0' resets stats. This is provided as a + fall-back in case F-keys do not function. The keys are + intercepted before returning control to libedit. */ + if (*c >= '0' && *c <= '9') { + const LineInfo *li; + + li = el_line(e); + if (li->lastchar == li->buffer) { + if (*c >= '1') { + display_screen(*c - '0' - 1); + return 0; + } + else { + cmd_parser_parse("reset stats", &input_curses); + return 0; + } + } + } + if (*c == '=') { + toggle_display_screen(); + return 0; + } + + /* Escape by itself is the first character used for more + complex escape sequences like F-keys. libedit can't be used + to detect both ESC as a unitary key and more complex + sequences starting ESC at the same time. */ + if (*c == 27 && !peek_stdin()) { + quit(); + return 0; + } + else if (*c != 9) { + tabbed = 0; + } + + return 1; +} + +static void proc_keyboard(struct input *input) +{ + const char *line; + const LineInfo *li; + HistEvent hist_event; + int len; + + line = el_gets(el, &len); + li = el_line(el); + + if (len == 0 || line == NULL) { + display_cmd("", 0, 0); + return; + } else if (len > 0) { + if (len == 1 && line[0] == '\n') { + display_print_page(); + el_set(el, EL_UNBUFFERED, 0); + el_set(el, EL_UNBUFFERED, 1); + return; + } + if (line[len-1] == '\n') { + if (hist) { + history(hist, &hist_event, H_ENTER, line); + } + + char *line2 = strndup(line, len); + line2[len - 1] = 0; /* replace \n */ + cmd_parser_parse(line2, input); + free(line2); + + el_set(el, EL_UNBUFFERED, 0); + el_set(el, EL_UNBUFFERED, 1); + display_cmd("", 0, 0); + return; + } + if (line[len-1] == 4) { + return; /* should quit*/ + } + } + else { + if (errno) { + return; + } + display_cmd("", 0, 0); + return; + } + display_cmd(line, len, li->cursor - li->buffer); +} + +static int key_f1(__attribute__((unused)) int ch) {display_screen(0); return CC_REDISPLAY;} +static int key_f2(__attribute__((unused)) int ch) {display_screen(1); return CC_REDISPLAY;} +static int key_f3(__attribute__((unused)) int ch) {display_screen(2); return CC_REDISPLAY;} +static int key_f4(__attribute__((unused)) int ch) {display_screen(3); return CC_REDISPLAY;} +static int key_f5(__attribute__((unused)) int ch) {display_screen(4); return CC_REDISPLAY;} +static int key_f6(__attribute__((unused)) int ch) {display_screen(5); return CC_REDISPLAY;} +static int key_f7(__attribute__((unused)) int ch) {display_screen(6); return CC_REDISPLAY;} +static int key_f8(__attribute__((unused)) int ch) {display_screen(7); return CC_REDISPLAY;} +static int key_f9(__attribute__((unused)) int ch) {display_screen(8); return CC_REDISPLAY;} +static int key_f10(__attribute__((unused)) int ch) {display_screen(9); return CC_REDISPLAY;} +static int key_f11(__attribute__((unused)) int ch) {display_screen(10); return CC_REDISPLAY;} +static int key_f12(__attribute__((unused)) int ch) {display_screen(11); return CC_REDISPLAY;} + +static int key_page_up(__attribute__((unused)) int ch) {display_page_up(); return CC_REDISPLAY;} +static int key_page_down(__attribute__((unused)) int ch) {display_page_down(); return CC_REDISPLAY;} + +static void setup_el(void) +{ + int pty; + FILE *dev_pty; + HistEvent hist_event; + + /* Open a pseudo-terminal for use in libedit. This is required + since the library checks if it is using a tty. If the file + descriptor does not represent a tty, the library disables + editing. */ + + pty = posix_openpt(O_RDWR); + /* TODO: On error (posix_openpt() < 0), fall-back to + non-libedit implementation. */ + grantpt(pty); + unlockpt(pty); + dev_pty = fdopen(pty, "wr"); + + el = el_init("", dev_pty, dev_pty, dev_pty); + + el_set(el, EL_EDITOR, "emacs"); + + el_set(el, EL_ADDFN, "complete", "Command completion", complete); + + el_set(el, EL_ADDFN, "key_f1", "Switch to screen 1", key_f1); + el_set(el, EL_ADDFN, "key_f2", "Switch to screen 2", key_f2); + el_set(el, EL_ADDFN, "key_f3", "Switch to screen 3", key_f3); + el_set(el, EL_ADDFN, "key_f4", "Switch to screen 4", key_f4); + el_set(el, EL_ADDFN, "key_f5", "Switch to screen 5", key_f5); + el_set(el, EL_ADDFN, "key_f6", "Switch to screen 6", key_f6); + el_set(el, EL_ADDFN, "key_f7", "Switch to screen 7", key_f7); + el_set(el, EL_ADDFN, "key_f8", "Switch to screen 8", key_f8); + el_set(el, EL_ADDFN, "key_f9", "Switch to screen 9", key_f5); + el_set(el, EL_ADDFN, "key_f10", "Switch to screen 10", key_f6); + el_set(el, EL_ADDFN, "key_f11", "Switch to screen 11", key_f7); + el_set(el, EL_ADDFN, "key_f12", "Switch to screen 12", key_f8); + + el_set(el, EL_ADDFN, "key_page_up", "Page up", key_page_up); + el_set(el, EL_ADDFN, "key_page_down", "Page down", key_page_down); + + el_set(el, EL_BIND, "^I", "complete", NULL); + el_set(el, EL_BIND, "^r", "em-inc-search-prev", NULL); + + el_set(el, EL_BIND, "^[[11~", "key_f1", NULL); + el_set(el, EL_BIND, "^[[12~", "key_f2", NULL); + el_set(el, EL_BIND, "^[[13~", "key_f3", NULL); + el_set(el, EL_BIND, "^[[14~", "key_f4", NULL); + el_set(el, EL_BIND, "^[[15~", "key_f5", NULL); + el_set(el, EL_BIND, "^[[17~", "key_f6", NULL); + el_set(el, EL_BIND, "^[[18~", "key_f7", NULL); + el_set(el, EL_BIND, "^[[19~", "key_f8", NULL); + el_set(el, EL_BIND, "^[[20~", "key_f9", NULL); + el_set(el, EL_BIND, "^[[21~", "key_f10", NULL); + el_set(el, EL_BIND, "^[[23~", "key_f11", NULL); + el_set(el, EL_BIND, "^[[24~", "key_f12", NULL); + + el_set(el, EL_BIND, "^[OP", "key_f1", NULL); + el_set(el, EL_BIND, "^[OQ", "key_f2", NULL); + el_set(el, EL_BIND, "^[OR", "key_f3", NULL); + el_set(el, EL_BIND, "^[OS", "key_f4", NULL); + + el_set(el, EL_BIND, "^[[5~", "key_page_up", NULL); + el_set(el, EL_BIND, "^[[6~", "key_page_down", NULL); + + hist = history_init(); + if (hist) { + history(hist, &hist_event, H_SETSIZE, 1000); + el_set(el, EL_HIST, history, hist); + } + el_set(el, EL_UNBUFFERED, 1); + el_set(el, EL_GETCFN, get_char); +} + +void reg_input_curses(void) +{ + setup_el(); + + input_curses.fd = fileno(stdin); + input_curses.proc_input = proc_keyboard; + input_curses.history = show_history; + + reg_input(&input_curses); +} + +void unreg_input_curses(void) +{ + history_end(hist); + el_end(el); + + unreg_input(&input_curses); +} diff --git a/VNFs/DPPD-PROX/input_curses.h b/VNFs/DPPD-PROX/input_curses.h new file mode 100644 index 00000000..8b682646 --- /dev/null +++ b/VNFs/DPPD-PROX/input_curses.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _INPUT_CURSES_H_ +#define _INPUT_CURSES_H_ + +void reg_input_curses(void); +void unreg_input_curses(void); + +#endif /* _INPUT_CURSES_H_ */ diff --git a/VNFs/DPPD-PROX/ip6_addr.h b/VNFs/DPPD-PROX/ip6_addr.h new file mode 100644 index 00000000..f9b56c19 --- /dev/null +++ b/VNFs/DPPD-PROX/ip6_addr.h @@ -0,0 +1,26 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _IP6_ADDR_H_ +#define _IP6_ADDR_H_ + +#include <inttypes.h> + +struct ipv6_addr { + uint8_t bytes[16]; +}; + +#endif /* _IP6_ADDR_H_ */ diff --git a/VNFs/DPPD-PROX/ip_subnet.c b/VNFs/DPPD-PROX/ip_subnet.c new file mode 100644 index 00000000..dc6ab1ac --- /dev/null +++ b/VNFs/DPPD-PROX/ip_subnet.c @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "ip_subnet.h" +#include "prox_assert.h" + +uint32_t ip4_subet_get_n_hosts(const struct ip4_subnet *sn) +{ + PROX_ASSERT(sn->prefix <= 32 && sn->prefix >= 1); + return 1 << (32 - sn->prefix); +} + +int ip4_subnet_to_host(const struct ip4_subnet *sn, uint32_t host_index, uint32_t *ret_ip) +{ + PROX_ASSERT(ip4_subnet_is_valid(sn)); + + if (host_index >= ip4_subet_get_n_hosts(sn)) { + return -1; + } + + *ret_ip = sn->ip + host_index; + return 0; +} + +int ip4_subnet_is_valid(const struct ip4_subnet *sn) +{ + if (sn->prefix == 0) { + return sn->ip == 0; + } + + return (sn->ip & ~(((int)(1 << 31)) >> (sn->prefix - 1))) == 0; +} diff --git a/VNFs/DPPD-PROX/ip_subnet.h b/VNFs/DPPD-PROX/ip_subnet.h new file mode 100644 index 00000000..126efb18 --- /dev/null +++ b/VNFs/DPPD-PROX/ip_subnet.h @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _IP_SUBNET_H_ +#define _IP_SUBNET_H_ + +#include <inttypes.h> + +struct ip4_subnet { + uint32_t ip; + uint8_t prefix; /* always in range [1,32] inclusive */ +}; + +struct ip6_subnet { + uint8_t ip[16]; + uint8_t prefix; /* always in range [1,128] inclusive */ +}; + +/* Returns number of hosts (assuming that network address and + broadcast address are both hosts) within the subnet. */ +uint32_t ip4_subet_get_n_hosts(const struct ip4_subnet *sn); + +/* Allows to get a specific host within a subnet. Note that the + network address and broadcast address are both considered to + "hosts". Setting host_index to 0 returns the network address and + setting the host_index to the last host within the subnet returns + the broadcast. To get all addresses with the subnet, loop + host_index from 0 to ip_subnet_get_n_hosts(). */ +int ip4_subnet_to_host(const struct ip4_subnet* sn, uint32_t host_index, uint32_t* ret_ip); + +/* Check if IP address is a network address (i.e. all bits outside the + prefix are set to 0). */ +int ip4_subnet_is_valid(const struct ip4_subnet* sn); + +#endif /* _IP_SUBNET_H_ */ diff --git a/VNFs/DPPD-PROX/kv_store_expire.h b/VNFs/DPPD-PROX/kv_store_expire.h new file mode 100644 index 00000000..c930af55 --- /dev/null +++ b/VNFs/DPPD-PROX/kv_store_expire.h @@ -0,0 +1,198 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_hash_crc.h> +#include <stdint.h> + +#include "prox_malloc.h" + +#define KV_STORE_BUCKET_DEPTH 8 + +struct kv_store_expire_entry { + /* if set to 0, the entry is disabled */ + uint64_t timeout; + /* Memory contains the key, followed by the actual value. */ + uint8_t mem[0]; +}; + +struct kv_store_expire { + size_t key_size; + size_t entry_size; + size_t bucket_mask; + size_t bucket_size; + uint64_t timeout; + + void (*expire)(void *entry_value); + + uint8_t mem[0]; +}; + +static struct kv_store_expire *kv_store_expire_create(uint32_t n_entries, size_t key_size, size_t value_size, int socket, void (*expire)(void *entry_value), uint64_t timeout) +{ + struct kv_store_expire *ret; + size_t memsize = 0; + size_t bucket_size; + size_t entry_size; + + if (!rte_is_power_of_2(n_entries)) + n_entries = rte_align32pow2(n_entries); + entry_size = sizeof(struct kv_store_expire_entry) + key_size + value_size; + + memsize += sizeof(struct kv_store_expire); + memsize += entry_size * n_entries; + + ret = prox_zmalloc(memsize, socket); + if (ret == NULL) + return NULL; + + ret->bucket_mask = n_entries / KV_STORE_BUCKET_DEPTH - 1; + ret->bucket_size = entry_size * KV_STORE_BUCKET_DEPTH; + ret->entry_size = entry_size; + ret->key_size = key_size; + ret->expire = expire; + ret->timeout = timeout; + + return ret; +} + +static size_t kv_store_expire_size(struct kv_store_expire *kv_store) +{ + return (kv_store->bucket_mask + 1) * KV_STORE_BUCKET_DEPTH; +} + +static void entry_set_timeout(struct kv_store_expire_entry *entry, uint64_t timeout) +{ + entry->timeout = timeout; +} + +static struct kv_store_expire_entry *entry_next(struct kv_store_expire *kv_store, struct kv_store_expire_entry *entry) +{ + return (struct kv_store_expire_entry *)((uint8_t *)entry + kv_store->entry_size); +} + +static void *entry_key(__attribute__((unused)) struct kv_store_expire *kv_store, struct kv_store_expire_entry *entry) +{ + return (uint8_t *)entry->mem; +} + +static void *entry_value(struct kv_store_expire *kv_store, struct kv_store_expire_entry *entry) +{ + return (uint8_t *)entry->mem + kv_store->key_size; +} + +static struct kv_store_expire_entry *kv_store_expire_get_first(struct kv_store_expire *kv_store) +{ + return (struct kv_store_expire_entry *)&kv_store->mem[0]; +} + +static struct kv_store_expire_entry *kv_store_expire_get_first_in_bucket(struct kv_store_expire *kv_store, void *key) +{ + uint32_t key_hash = rte_hash_crc(key, kv_store->key_size, 0); + uint32_t bucket_idx = key_hash & kv_store->bucket_mask; + + return (struct kv_store_expire_entry *)&kv_store->mem[bucket_idx * kv_store->bucket_size]; +} + +static int entry_key_matches(struct kv_store_expire *kv_store, struct kv_store_expire_entry *entry, void *key) +{ + return !memcmp(entry_key(kv_store, entry), key, kv_store->key_size); +} + +static struct kv_store_expire_entry *kv_store_expire_get(struct kv_store_expire *kv_store, void *key, uint64_t now) +{ + struct kv_store_expire_entry *entry = kv_store_expire_get_first_in_bucket(kv_store, key); + + for (int i = 0; i < KV_STORE_BUCKET_DEPTH; ++i) { + if (entry->timeout && entry->timeout >= now) { + if (entry_key_matches(kv_store, entry, key)) { + entry->timeout = now + kv_store->timeout; + return entry; + } + } + entry = entry_next(kv_store, entry); + } + return NULL; +} + +static struct kv_store_expire_entry *kv_store_expire_put(struct kv_store_expire *kv_store, void *key, uint64_t now) +{ + struct kv_store_expire_entry *e = kv_store_expire_get_first_in_bucket(kv_store, key); + + for (int i = 0; i < KV_STORE_BUCKET_DEPTH; ++i) { + if (e->timeout && e->timeout >= now) { + e = entry_next(kv_store, e); + continue; + } + if (!e->timeout) { + kv_store->expire(entry_value(kv_store, e)); + } + + rte_memcpy(entry_key(kv_store, e), key, kv_store->key_size); + e->timeout = now + kv_store->timeout; + return e; + } + + return NULL; +} + +/* If the entry is not found, a put operation is tried and if that + succeeds, that entry is returned. The bucket is full if NULL Is + returned. */ +static struct kv_store_expire_entry *kv_store_expire_get_or_put(struct kv_store_expire *kv_store, void *key, uint64_t now) +{ + struct kv_store_expire_entry *entry = kv_store_expire_get_first_in_bucket(kv_store, key); + struct kv_store_expire_entry *v = NULL; + + for (int i = 0; i < KV_STORE_BUCKET_DEPTH; ++i) { + if (entry->timeout && entry->timeout >= now) { + if (entry_key_matches(kv_store, entry, key)) { + entry->timeout = now + kv_store->timeout; + return entry; + } + } + else { + v = v? v : entry; + } + entry = entry_next(kv_store, entry); + } + + if (v) { + if (entry->timeout) + kv_store->expire(entry_value(kv_store, v)); + rte_memcpy(entry_key(kv_store, v), key, kv_store->key_size); + v->timeout = now + kv_store->timeout; + return v; + } + + return NULL; +} + +static size_t kv_store_expire_expire_all(struct kv_store_expire *kv_store) +{ + struct kv_store_expire_entry *entry = kv_store_expire_get_first(kv_store); + size_t elems = kv_store_expire_size(kv_store); + size_t expired = 0; + + do { + if (entry->timeout) { + kv_store->expire(entry_value(kv_store, entry)); + entry->timeout = 0; + expired++; + } + entry = entry_next(kv_store, entry); + } while (--elems); + return expired; +} diff --git a/VNFs/DPPD-PROX/lconf.c b/VNFs/DPPD-PROX/lconf.c new file mode 100644 index 00000000..88d8f4f9 --- /dev/null +++ b/VNFs/DPPD-PROX/lconf.c @@ -0,0 +1,355 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "prox_malloc.h" +#include "lconf.h" +#include "rx_pkt.h" +#include "tx_pkt.h" +#include "log.h" +#include "quit.h" +#include "prox_cfg.h" + +struct lcore_cfg *lcore_cfg; +/* only used at initialization time */ +struct lcore_cfg lcore_cfg_init[RTE_MAX_LCORE]; + +static int core_targ_next_from(struct lcore_cfg **lconf, struct task_args **targ, struct lcore_cfg *lcore_cfg, const int with_master) +{ + uint32_t lcore_id, task_id; + + if (*lconf && *targ) { + lcore_id = *lconf - lcore_cfg; + task_id = *targ - lcore_cfg[lcore_id].targs; + + if (task_id + 1 < lcore_cfg[lcore_id].n_tasks_all) { + *targ = &lcore_cfg[lcore_id].targs[task_id + 1]; + return 0; + } else { + if (prox_core_next(&lcore_id, with_master)) + return -1; + *lconf = &lcore_cfg[lcore_id]; + *targ = &lcore_cfg[lcore_id].targs[0]; + return 0; + } + } else { + lcore_id = -1; + + if (prox_core_next(&lcore_id, with_master)) + return -1; + *lconf = &lcore_cfg[lcore_id]; + *targ = &lcore_cfg[lcore_id].targs[0]; + return 0; + } +} + +int core_targ_next(struct lcore_cfg **lconf, struct task_args **targ, const int with_master) +{ + return core_targ_next_from(lconf, targ, lcore_cfg, with_master); +} + +int core_targ_next_early(struct lcore_cfg **lconf, struct task_args **targ, const int with_master) +{ + return core_targ_next_from(lconf, targ, lcore_cfg_init, with_master); +} + +struct task_args *core_targ_get(uint32_t lcore_id, uint32_t task_id) +{ + return &lcore_cfg[lcore_id].targs[task_id]; +} + +void lcore_cfg_alloc_hp(void) +{ + size_t mem_size = RTE_MAX_LCORE * sizeof(struct lcore_cfg); + + lcore_cfg = prox_zmalloc(mem_size, rte_socket_id()); + PROX_PANIC(lcore_cfg == NULL, "Could not allocate memory for core control structures\n"); + rte_memcpy(lcore_cfg, lcore_cfg_init, mem_size); + + /* get thread ID for master core */ + lcore_cfg[rte_lcore_id()].thread_id = pthread_self(); +} + +int lconf_run(__attribute__((unused)) void *dummy) +{ + uint32_t lcore_id = rte_lcore_id(); + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + /* get thread ID, and set cancellation type to asynchronous */ + lconf->thread_id = pthread_self(); + int ret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (ret != 0) + plog_warn("pthread_setcanceltype() failed on core %u: %i\n", lcore_id, ret); + + plog_info("Entering main loop on core %u\n", lcore_id); + return lconf->thread_x(lconf); +} + +static void msg_stop(struct lcore_cfg *lconf) +{ + int idx = -1; + struct task_base *t = NULL; + + if (lconf->msg.task_id == -1) { + for (int i = 0; i < lconf->n_tasks_all; ++i) { + if (lconf->task_is_running[i]) { + lconf->task_is_running[i] = 0; + t = lconf->tasks_all[i]; + if (t->aux->stop) + t->aux->stop(t); + } + } + lconf->n_tasks_run = 0; + + if (t && t->aux->stop_last) + t->aux->stop_last(t); + } + else { + for (int i = 0; i < lconf->n_tasks_run; ++i) { + if (lconf_get_task_id(lconf, lconf->tasks_run[i]) == lconf->msg.task_id) { + idx = i; + } + else if (idx != -1) { + lconf->tasks_run[idx] = lconf->tasks_run[i]; + + idx++; + } + } + lconf->task_is_running[lconf->msg.task_id] = 0; + + t = lconf->tasks_all[lconf->msg.task_id]; + if (t->aux->stop) + t->aux->stop(t); + lconf->n_tasks_run--; + if (lconf->n_tasks_run == 0 && t->aux->stop_last) + t->aux->stop_last(t); + } +} + +static void msg_start(struct lcore_cfg *lconf) +{ + int idx = 1; + struct task_base *t = NULL; + + if (lconf->msg.task_id == -1) { + for (int i = 0; i < lconf->n_tasks_all; ++i) { + t = lconf->tasks_run[i] = lconf->tasks_all[i]; + lconf->task_is_running[i] = 1; + if (lconf->n_tasks_run == 0 && t->aux->start_first) { + t->aux->start_first(t); + lconf->n_tasks_run = 1; + } + if (t->aux->start) + t->aux->start(t); + } + lconf->n_tasks_run = lconf->n_tasks_all; + } + else if (lconf->n_tasks_run == 0) { + t = lconf->tasks_run[0] = lconf->tasks_all[lconf->msg.task_id]; + lconf->n_tasks_run = 1; + lconf->task_is_running[lconf->msg.task_id] = 1; + + if (t->aux->start_first) + t->aux->start_first(t); + if (t->aux->start) + t->aux->start(t); + } + else { + for (int i = lconf->n_tasks_run - 1; i >= 0; --i) { + idx = lconf_get_task_id(lconf, lconf->tasks_run[i]); + if (idx == lconf->msg.task_id) { + break; + } + else if (idx > lconf->msg.task_id) { + lconf->tasks_run[i + 1] = lconf->tasks_run[i]; + if (i == 0) { + lconf->tasks_run[i] = lconf->tasks_all[lconf->msg.task_id]; + lconf->n_tasks_run++; + break; + } + } + else { + lconf->tasks_run[i + 1] = lconf->tasks_all[lconf->msg.task_id]; + lconf->n_tasks_run++; + break; + } + } + lconf->task_is_running[lconf->msg.task_id] = 1; + + if (lconf->tasks_all[lconf->msg.task_id]->aux->start) + lconf->tasks_all[lconf->msg.task_id]->aux->start(lconf->tasks_all[lconf->msg.task_id]); + } +} + +int lconf_do_flags(struct lcore_cfg *lconf) +{ + struct task_base *t; + int ret = 0; + + switch (lconf->msg.type) { + case LCONF_MSG_STOP: + msg_stop(lconf); + ret = -1; + break; + case LCONF_MSG_START: + msg_start(lconf); + ret = -1; + break; + case LCONF_MSG_DUMP_RX: + case LCONF_MSG_DUMP_TX: + case LCONF_MSG_DUMP: + t = lconf->tasks_all[lconf->msg.task_id]; + + if (lconf->msg.val) { + if (lconf->msg.type == LCONF_MSG_DUMP || + lconf->msg.type == LCONF_MSG_DUMP_RX) { + t->aux->task_rt_dump.n_print_rx = lconf->msg.val; + + task_base_add_rx_pkt_function(t, rx_pkt_dump); + } + + if (lconf->msg.type == LCONF_MSG_DUMP || + lconf->msg.type == LCONF_MSG_DUMP_TX) { + t->aux->task_rt_dump.n_print_tx = lconf->msg.val; + if (t->aux->tx_pkt_orig) + t->tx_pkt = t->aux->tx_pkt_orig; + t->aux->tx_pkt_orig = t->tx_pkt; + t->tx_pkt = tx_pkt_dump; + } + } + break; + case LCONF_MSG_TRACE: + t = lconf->tasks_all[lconf->msg.task_id]; + + if (lconf->msg.val) { + t->aux->task_rt_dump.n_trace = lconf->msg.val; + + if (task_base_get_original_rx_pkt_function(t) != rx_pkt_dummy) { + task_base_add_rx_pkt_function(t, rx_pkt_trace); + if (t->aux->tx_pkt_orig) + t->tx_pkt = t->aux->tx_pkt_orig; + t->aux->tx_pkt_orig = t->tx_pkt; + t->tx_pkt = tx_pkt_trace; + } else { + t->aux->task_rt_dump.n_print_tx = lconf->msg.val; + if (t->aux->tx_pkt_orig) + t->tx_pkt = t->aux->tx_pkt_orig; + t->aux->tx_pkt_orig = t->tx_pkt; + t->tx_pkt = tx_pkt_dump; + } + } + break; + case LCONF_MSG_RX_DISTR_START: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + task_base_add_rx_pkt_function(t, rx_pkt_distr); + memset(t->aux->rx_bucket, 0, sizeof(t->aux->rx_bucket)); + lconf->flags |= LCONF_FLAG_RX_DISTR_ACTIVE; + } + break; + case LCONF_MSG_TX_DISTR_START: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + + t->aux->tx_pkt_orig = t->tx_pkt; + t->tx_pkt = tx_pkt_distr; + memset(t->aux->tx_bucket, 0, sizeof(t->aux->tx_bucket)); + lconf->flags |= LCONF_FLAG_TX_DISTR_ACTIVE; + } + break; + case LCONF_MSG_RX_DISTR_STOP: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + task_base_del_rx_pkt_function(t, rx_pkt_distr); + lconf->flags &= ~LCONF_FLAG_RX_DISTR_ACTIVE; + } + break; + case LCONF_MSG_TX_DISTR_STOP: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + if (t->aux->tx_pkt_orig) { + t->tx_pkt = t->aux->tx_pkt_orig; + t->aux->tx_pkt_orig = NULL; + lconf->flags &= ~LCONF_FLAG_TX_DISTR_ACTIVE; + } + } + break; + case LCONF_MSG_RX_DISTR_RESET: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + + memset(t->aux->rx_bucket, 0, sizeof(t->aux->rx_bucket)); + } + break; + case LCONF_MSG_TX_DISTR_RESET: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + + memset(t->aux->tx_bucket, 0, sizeof(t->aux->tx_bucket)); + } + break; + case LCONF_MSG_RX_BW_START: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + task_base_add_rx_pkt_function(t, rx_pkt_bw); + lconf->flags |= LCONF_FLAG_RX_BW_ACTIVE; + } + break; + case LCONF_MSG_RX_BW_STOP: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + task_base_del_rx_pkt_function(t, rx_pkt_bw); + lconf->flags &= ~LCONF_FLAG_RX_BW_ACTIVE; + } + break; + case LCONF_MSG_TX_BW_START: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + + t->aux->tx_pkt_orig = t->tx_pkt; + t->tx_pkt = tx_pkt_bw; + lconf->flags |= LCONF_FLAG_TX_BW_ACTIVE; + } + break; + case LCONF_MSG_TX_BW_STOP: + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + t = lconf->tasks_all[task_id]; + if (t->aux->tx_pkt_orig) { + t->tx_pkt = t->aux->tx_pkt_orig; + t->aux->tx_pkt_orig = NULL; + lconf->flags &= ~LCONF_FLAG_TX_BW_ACTIVE; + } + } + break; + } + + lconf_unset_req(lconf); + return ret; +} + +int lconf_get_task_id(const struct lcore_cfg *lconf, const struct task_base *task) +{ + for (int i = 0; i < lconf->n_tasks_all; ++i) { + if (lconf->tasks_all[i] == task) + return i; + } + + return -1; +} + +int lconf_task_is_running(const struct lcore_cfg *lconf, uint8_t task_id) +{ + return lconf->task_is_running[task_id]; +} diff --git a/VNFs/DPPD-PROX/lconf.h b/VNFs/DPPD-PROX/lconf.h new file mode 100644 index 00000000..4bfa705d --- /dev/null +++ b/VNFs/DPPD-PROX/lconf.h @@ -0,0 +1,145 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _LCONF_H_ +#define _LCONF_H_ + +#include "task_init.h" +#include "stats.h" + +enum lconf_msg_type { + LCONF_MSG_STOP, + LCONF_MSG_START, + LCONF_MSG_DUMP, + LCONF_MSG_TRACE, + LCONF_MSG_DUMP_RX, + LCONF_MSG_DUMP_TX, + LCONF_MSG_RX_DISTR_START, + LCONF_MSG_RX_DISTR_STOP, + LCONF_MSG_RX_DISTR_RESET, + LCONF_MSG_TX_DISTR_START, + LCONF_MSG_TX_DISTR_STOP, + LCONF_MSG_TX_DISTR_RESET, + LCONF_MSG_RX_BW_START, + LCONF_MSG_RX_BW_STOP, + LCONF_MSG_TX_BW_START, + LCONF_MSG_TX_BW_STOP, +}; + +struct lconf_msg { + /* Set by master core (if not set), unset by worker after consumption. */ + uint32_t req; + enum lconf_msg_type type; + int task_id; + int val; +}; + +#define LCONF_FLAG_RX_DISTR_ACTIVE 0x00000001 +#define LCONF_FLAG_RUNNING 0x00000002 +#define LCONF_FLAG_TX_DISTR_ACTIVE 0x00000004 +#define LCONF_FLAG_RX_BW_ACTIVE 0x00000008 +#define LCONF_FLAG_TX_BW_ACTIVE 0x00000010 + +struct lcore_cfg { + /* All tasks running at the moment. This is empty when the core is stopped. */ + struct task_base *tasks_run[MAX_TASKS_PER_CORE]; + uint8_t n_tasks_run; + + void (*flush_queues[MAX_TASKS_PER_CORE])(struct task_base *tbase); + + void (*period_func)(void *data); + void *period_data; + /* call periodic_func after periodic_timeout cycles */ + uint64_t period_timeout; + + uint64_t ctrl_timeout; + void (*ctrl_func_m[MAX_TASKS_PER_CORE])(struct task_base *tbase, void **data, uint16_t n_msgs); + struct rte_ring *ctrl_rings_m[MAX_TASKS_PER_CORE]; + + void (*ctrl_func_p[MAX_TASKS_PER_CORE])(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + struct rte_ring *ctrl_rings_p[MAX_TASKS_PER_CORE]; + + struct lconf_msg msg __attribute__((aligned(4))); + struct task_base *tasks_all[MAX_TASKS_PER_CORE]; + int task_is_running[MAX_TASKS_PER_CORE]; + uint8_t n_tasks_all; + pthread_t thread_id; + + /* Following variables are not accessed in main loop */ + uint32_t flags; + uint8_t active_task; + uint8_t id; + char name[MAX_NAME_SIZE]; + struct task_args targs[MAX_TASKS_PER_CORE]; + int (*thread_x)(struct lcore_cfg *lconf); + uint32_t cache_set; +} __rte_cache_aligned; + +extern struct lcore_cfg *lcore_cfg; +extern struct lcore_cfg lcore_cfg_init[]; + +/* This function is only run on low load (when no bulk was sent within + last drain_timeout (16kpps if DRAIN_TIMEOUT = 2 ms) */ +static inline void lconf_flush_all_queues(struct lcore_cfg *lconf) +{ + struct task_base *task; + + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + task = lconf->tasks_all[task_id]; + if (!(task->flags & FLAG_TX_FLUSH) || (task->flags & FLAG_NEVER_FLUSH)) { + task->flags |= FLAG_TX_FLUSH; + continue; + } + lconf->flush_queues[task_id](task); + } +} + +static inline void lconf_set_req(struct lcore_cfg *lconf) +{ + (*(volatile uint32_t *)&lconf->msg.req) = 1; +} + +static inline void lconf_unset_req(struct lcore_cfg *lconf) +{ + (*(volatile uint32_t *)&lconf->msg.req) = 0; +} + +static inline int lconf_is_req(struct lcore_cfg *lconf) +{ + return (*(volatile uint32_t *)&lconf->msg.req); +} + +/* Returns non-zero when terminate has been requested */ +int lconf_do_flags(struct lcore_cfg *lconf); + +int lconf_get_task_id(const struct lcore_cfg *lconf, const struct task_base *task); +int lconf_task_is_running(const struct lcore_cfg *lconf, uint8_t task_id); + +int lconf_run(void *dummy); + +void lcore_cfg_alloc_hp(void); + +/* Returns the next active lconf/targ pair. If *lconf = NULL, the + first active lconf/targ pair is returned. If the last lconf/targ + pair is passed, the function returns non-zero. */ +int core_targ_next(struct lcore_cfg **lconf, struct task_args **targ, const int with_master); +/* Same as above, but uses non-huge page memory (used before + lcore_cfg_alloc_hp is called). */ +int core_targ_next_early(struct lcore_cfg **lconf, struct task_args **targ, const int with_master); + +struct task_args *core_targ_get(uint32_t lcore_id, uint32_t task_id); + +#endif /* _LCONF_H_ */ diff --git a/VNFs/DPPD-PROX/local_mbuf.h b/VNFs/DPPD-PROX/local_mbuf.h new file mode 100644 index 00000000..c65086cc --- /dev/null +++ b/VNFs/DPPD-PROX/local_mbuf.h @@ -0,0 +1,62 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _LOCAL_MBUF_H_ +#define _LOCAL_MBUF_H_ +#define LOCAL_MBUF_COUNT 64 + +struct local_mbuf { + struct rte_mempool *mempool; + uint32_t n_new_pkts; + struct rte_mbuf *new_pkts[LOCAL_MBUF_COUNT]; +}; + +static struct rte_mbuf **local_mbuf_take(struct local_mbuf *local_mbuf, uint32_t count) +{ + PROX_ASSERT(local_mbuf->n_new_pkts >= count); + + const uint32_t start_pos = local_mbuf->n_new_pkts - count; + struct rte_mbuf **ret = &local_mbuf->new_pkts[start_pos]; + + local_mbuf->n_new_pkts -= count; + return ret; +} + +static int local_mbuf_refill(struct local_mbuf *local_mbuf) +{ + const uint32_t fill = LOCAL_MBUF_COUNT - local_mbuf->n_new_pkts; + struct rte_mbuf **fill_mbuf = &local_mbuf->new_pkts[local_mbuf->n_new_pkts]; + + if (rte_mempool_get_bulk(local_mbuf->mempool, (void **)fill_mbuf, fill) < 0) + return -1; + local_mbuf->n_new_pkts += fill; + return 0; +} + +/* Ensures that count or more mbufs are available. Returns pointer to + count allocated mbufs or NULL if not enough mbufs are available. */ +static struct rte_mbuf **local_mbuf_refill_and_take(struct local_mbuf *local_mbuf, uint32_t count) +{ + PROX_ASSERT(count <= LOCAL_MBUF_COUNT); + if (local_mbuf->n_new_pkts >= count) + return local_mbuf_take(local_mbuf, count); + + if (local_mbuf_refill(local_mbuf) == 0) + return local_mbuf_take(local_mbuf, count); + return NULL; +} + +#endif /* _LOCAL_MBUF_H_ */ diff --git a/VNFs/DPPD-PROX/log.c b/VNFs/DPPD-PROX/log.c new file mode 100644 index 00000000..cd8ee002 --- /dev/null +++ b/VNFs/DPPD-PROX/log.c @@ -0,0 +1,398 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <sys/types.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> +#include <rte_cycles.h> +#include <rte_lcore.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_mbuf.h> + +#include "log.h" +#include "display.h" +#include "etypes.h" +#include "prox_cfg.h" + +static pthread_mutex_t file_mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +int log_lvl = PROX_MAX_LOG_LVL; +static uint64_t tsc_off; +static FILE *fp; +static int n_warnings = 0; +char last_warn[5][1024]; +int get_n_warnings(void) +{ +#if PROX_MAX_LOG_LVL < PROX_LOG_WARN + return -1; +#endif + return n_warnings; +} + +const char *get_warning(int i) +{ +#if PROX_MAX_LOG_LVL < PROX_LOG_WARN + return NULL; +#endif + if (i > 0 || i < -4) + return NULL; + return last_warn[(n_warnings - 1 + i + 5) % 5]; +} + +static void store_warning(const char *warning) +{ + strncpy(last_warn[n_warnings % 5], warning, sizeof(last_warn[0])); + n_warnings++; +} + +void plog_init(const char *log_name, int log_name_pid) +{ + pid_t pid; + char buf[128]; + + if (*log_name == 0) { + if (log_name_pid) + snprintf(buf, sizeof(buf), "%s-%u.log", "prox", getpid()); + else + strncpy(buf, "prox.log", sizeof(buf)); + } + else { + strncpy(buf, log_name, sizeof(buf)); + } + + fp = fopen(buf, "w"); + + tsc_off = rte_rdtsc() + 2500000000; +} + +int plog_set_lvl(int lvl) +{ + if (lvl <= PROX_MAX_LOG_LVL) { + log_lvl = lvl; + return 0; + } + + return -1; +} + +static void file_lock(void) +{ + pthread_mutex_lock(&file_mtx); +} + +static void file_unlock(void) +{ + pthread_mutex_unlock(&file_mtx); +} + +void file_print(const char *str) +{ + file_lock(); + if (fp != NULL) { + fputs(str, fp); + fflush(fp); + } + file_unlock(); +} +static void plog_buf(const char* buf) +{ + if (prox_cfg.logbuf) { + file_lock(); + if (prox_cfg.logbuf_pos + strlen(buf) + 1 < prox_cfg.logbuf_size) { + memcpy(prox_cfg.logbuf + prox_cfg.logbuf_pos, buf, strlen(buf)); + prox_cfg.logbuf_pos += strlen(buf); + } + file_unlock(); + } else { + file_print(buf); +#ifdef PROX_STATS + display_print(buf); +#else + /* ncurses never initialized */ + fputs(buf, stdout); + fflush(stdout); +#endif + } +} + +static const char* lvl_to_str(int lvl, int always) +{ + switch (lvl) { + case PROX_LOG_ERR: return "error "; + case PROX_LOG_WARN: return "warn "; + case PROX_LOG_INFO: return always? "info " : ""; + case PROX_LOG_DBG: return "debug "; + default: return "?"; + } +} + +#define DUMP_PKT_LEN 128 +static int dump_pkt(char *dst, size_t dst_size, const struct rte_mbuf *mbuf) +{ + const struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, const struct ether_hdr *); + const struct ipv4_hdr *dpip = (const struct ipv4_hdr *)(peth + 1); + const uint8_t *pkt_bytes = (const uint8_t *)peth; + const uint16_t len = rte_pktmbuf_pkt_len(mbuf); + size_t str_len = 0; + + if (peth->ether_type == ETYPE_IPv4) + str_len = snprintf(dst, dst_size, "pkt_len=%u, Eth=%x, Proto=%#06x", + len, peth->ether_type, dpip->next_proto_id); + else + str_len = snprintf(dst, dst_size, "pkt_len=%u, Eth=%x", + len, peth->ether_type); + + for (uint16_t i = 0; i < len && i < DUMP_PKT_LEN && str_len < dst_size; ++i) { + if (i % 16 == 0) { + str_len += snprintf(dst + str_len, dst_size - str_len, "\n%04x ", i); + } + else if (i % 8 == 0) { + str_len += snprintf(dst + str_len, dst_size - str_len, " "); + } + str_len += snprintf(dst + str_len, dst_size - str_len, "%02x ", pkt_bytes[i]); + } + if (str_len < dst_size) + snprintf(dst + str_len, dst_size - str_len, "\n"); + return str_len + 1; +} + +static int vplog(int lvl, const char *format, va_list ap, const struct rte_mbuf *mbuf, int extended) +{ + char buf[32768]; + uint64_t hz, rtime_tsc, rtime_sec, rtime_usec; + int ret = 0; + + if (lvl > log_lvl) + return ret; + + if (format == NULL && mbuf == NULL) + return ret; + + *buf = 0; + if (extended) { + hz = rte_get_tsc_hz(); + rtime_tsc = rte_rdtsc() - tsc_off; + rtime_sec = rtime_tsc / hz; + rtime_usec = (rtime_tsc - rtime_sec * hz) / (hz / 1000000); + ret += snprintf(buf, sizeof(buf) - ret, "%2"PRIu64".%06"PRIu64" C%u %s%s", + rtime_sec, rtime_usec, rte_lcore_id(), lvl_to_str(lvl, 1), format? " " : ""); + } + else { + ret += snprintf(buf, sizeof(buf) - ret, "%s%s", lvl_to_str(lvl, 0), format? " " : ""); + } + + if (format) { + ret--; + ret += vsnprintf(buf + ret, sizeof(buf) - ret, format, ap); + } + + if (mbuf) { + ret--; + ret += dump_pkt(buf + ret, sizeof(buf) - ret, mbuf); + } + plog_buf(buf); + + if (lvl == PROX_LOG_WARN) { + store_warning(buf); + } + return ret; +} + +#if PROX_MAX_LOG_LVL >= PROX_LOG_INFO +int plog_info(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_INFO, fmt, ap, NULL, 0); + va_end(ap); + return ret; +} + +int plogx_info(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_INFO, fmt, ap, NULL, 1); + va_end(ap); + return ret; +} + +int plogd_info(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_INFO, fmt, ap, mbuf, 0); + va_end(ap); + return ret; +} + +int plogdx_info(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_INFO, fmt, ap, mbuf, 1); + va_end(ap); + return ret; +} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_ERR +int plog_err(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_ERR, fmt, ap, NULL, 0); + va_end(ap); + return ret; +} + +int plogx_err(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_ERR, fmt, ap, NULL, 1); + va_end(ap); + return ret; +} + +int plogd_err(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_ERR, fmt, ap, mbuf, 1); + va_end(ap); + return ret; +} + +int plogdx_err(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_ERR, fmt, ap, mbuf, 1); + va_end(ap); + + return ret; +} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_WARN +int plog_warn(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_WARN, fmt, ap, NULL, 0); + va_end(ap); + return ret; +} + +int plogx_warn(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_WARN, fmt, ap, NULL, 1); + va_end(ap); + return ret; +} + +int plogd_warn(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_WARN, fmt, ap, mbuf, 0); + va_end(ap); + return ret; +} + +int plogdx_warn(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_WARN, fmt, ap, mbuf, 1); + va_end(ap); + return ret; +} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_DBG +int plog_dbg(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_DBG, fmt, ap, NULL, 0); + va_end(ap); + return ret; +} + +int plogx_dbg(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_DBG, fmt, ap, NULL, 1); + va_end(ap); + return ret; +} + +int plogd_dbg(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_DBG, fmt, ap, mbuf, 0); + va_end(ap); + return ret; +} + +int plogdx_dbg(const struct rte_mbuf *mbuf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vplog(PROX_LOG_DBG, fmt, ap, mbuf, 1); + va_end(ap); + return ret; +} +#endif diff --git a/VNFs/DPPD-PROX/log.h b/VNFs/DPPD-PROX/log.h new file mode 100644 index 00000000..a5dcf47a --- /dev/null +++ b/VNFs/DPPD-PROX/log.h @@ -0,0 +1,88 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#define PROX_LOG_ERR 0 +#define PROX_LOG_WARN 1 +#define PROX_LOG_INFO 2 +#define PROX_LOG_DBG 3 + +#if PROX_MAX_LOG_LVL > PROX_LOG_DBG +#error Highest supported log level is 3 +#endif + +int get_n_warnings(void); +/* Return previous warnings, only stores last 5 warnings and invalid i return NULL*/ +const char* get_warning(int i); + +struct rte_mbuf; + +#if PROX_MAX_LOG_LVL >= PROX_LOG_ERR +int plog_err(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogx_err(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogd_err(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +int plogdx_err(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +#else +__attribute__((format(printf, 1, 2))) static inline int plog_err(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 1, 2))) static inline int plogx_err(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogd_err(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogdx_err(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_WARN +int plog_warn(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogx_warn(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogd_warn(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +int plogdx_warn(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +#else +__attribute__((format(printf, 1, 2))) static inline int plog_warn(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 1, 2))) static inline int plogx_warn(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogd_warn(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogdx_warn(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_INFO +int plog_info(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogx_info(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogd_info(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +int plogdx_info(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +#else +__attribute__((format(printf, 1, 2))) static inline int plog_info(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 1, 2))) static inline int plogx_info(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogd_info(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogdx_info(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +#endif + +#if PROX_MAX_LOG_LVL >= PROX_LOG_DBG +int plog_dbg(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogx_dbg(const char *fmt, ...) __attribute__((format(printf, 1, 2), cold)); +int plogd_dbg(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +int plogdx_dbg(const struct rte_mbuf *mbuf, const char *fmt, ...) __attribute__((format(printf, 2, 3), cold)); +#else +__attribute__((format(printf, 1, 2))) static inline int plog_dbg(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 1, 2))) static inline int plogx_dbg(__attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogd_dbg(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +__attribute__((format(printf, 2, 3))) static inline int plogdx_dbg(__attribute__((unused)) const struct rte_mbuf *mbuf, __attribute__((unused)) const char *fmt, ...) {return 0;} +#endif + +void plog_init(const char *log_name, int log_name_pid); +void file_print(const char *str); + +int plog_set_lvl(int lvl); + +#endif /* _LOG_H_ */ diff --git a/VNFs/DPPD-PROX/lua_compat.h b/VNFs/DPPD-PROX/lua_compat.h new file mode 100644 index 00000000..c8c21225 --- /dev/null +++ b/VNFs/DPPD-PROX/lua_compat.h @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _LUA_COMPAT_H_ +#define _LUA_COMPAT_H_ + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +#if LUA_VERSION_NUM < 503 +#include <float.h> +static int lua_isinteger(lua_State *L, int idx) +{ + if (!lua_isnumber(L, idx)) { + return -1; + } + + double whole = lua_tonumber(L, idx); + whole -= lua_tointeger(L, idx); + return whole < DBL_EPSILON && whole >= -DBL_EPSILON ; +} +#endif + +#if LUA_VERSION_NUM < 502 +static int lua_len(lua_State *L, int idx) +{ + int len = lua_objlen(L, idx); + + lua_pushnumber(L, len); + return len; +} +#endif + +#endif /* _LUA_COMPAT_H_ */ diff --git a/VNFs/DPPD-PROX/main.c b/VNFs/DPPD-PROX/main.c new file mode 100644 index 00000000..28533c78 --- /dev/null +++ b/VNFs/DPPD-PROX/main.c @@ -0,0 +1,993 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <locale.h> +#include <unistd.h> +#include <signal.h> + +#include <rte_cycles.h> +#include <rte_atomic.h> +#include <rte_table_hash.h> +#include <rte_memzone.h> +#include <rte_errno.h> + +#include "prox_malloc.h" +#include "run.h" +#include "main.h" +#include "log.h" +#include "quit.h" +#include "clock.h" +#include "defines.h" +#include "version.h" +#include "prox_args.h" +#include "prox_assert.h" +#include "prox_cfg.h" +#include "prox_shared.h" +#include "prox_port_cfg.h" +#include "toeplitz.h" +#include "hash_utils.h" +#include "handle_lb_net.h" +#include "prox_cksum.h" +#include "thread_nop.h" +#include "thread_generic.h" +#include "thread_pipeline.h" +#include "cqm.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +uint8_t lb_nb_txrings = 0xff; +struct rte_ring *ctrl_rings[RTE_MAX_LCORE*MAX_TASKS_PER_CORE]; + +static void __attribute__((noreturn)) prox_usage(const char *prgname) +{ + plog_info("\nUsage: %s [-f CONFIG_FILE] [-a|-e] [-m|-s|-i] [-w DEF] [-u] [-t]\n" + "\t-f CONFIG_FILE : configuration file to load, ./prox.cfg by default\n" + "\t-l LOG_FILE : log file name, ./prox.log by default\n" + "\t-p : include PID in log file name if default log file is used\n" + "\t-o DISPLAY: Set display to use, can be 'curses' (default), 'cli' or 'none'\n" + "\t-v verbosity : initial logging verbosity\n" + "\t-a : autostart all cores (by default)\n" + "\t-e : don't autostart\n" + "\t-n : Create NULL devices instead of using PCI devices, useful together with -i\n" + "\t-m : list supported task modes and exit\n" + "\t-s : check configuration file syntax and exit\n" + "\t-i : check initialization sequence and exit\n" + "\t-u : Listen on UDS /tmp/prox.sock\n" + "\t-t : Listen on TCP port 8474\n" + "\t-q : Pass argument to Lua interpreter, useful to define variables\n" + "\t-w : define variable using syntax varname=value\n" + "\t takes precedence over variables defined in CONFIG_FILE\n" + "\t-k : Log statistics to file \"stats_dump\" in current directory\n" + "\t-d : Run as daemon, the parent process will block until PROX is not initialized\n" + "\t-z : Ignore CPU topology, implies -i\n" + "\t-r : Change initial screen refresh rate. If set to a lower than 0.001 seconds,\n" + "\t screen refreshing will be disabled\n" + , prgname); + exit(EXIT_FAILURE); +} + +static void check_mixed_normal_pipeline(void) +{ + struct lcore_cfg *lconf = NULL; + uint32_t lcore_id = -1; + + while (prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + + int all_thread_nop = 1; + int generic = 0; + int pipeline = 0; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + all_thread_nop = all_thread_nop && + targ->task_init->thread_x == thread_nop; + + pipeline = pipeline || targ->task_init->thread_x == thread_pipeline; + generic = generic || targ->task_init->thread_x == thread_generic; + } + PROX_PANIC(generic && pipeline, "Can't run both pipeline and normal thread on same core\n"); + + if (all_thread_nop) + lconf->thread_x = thread_nop; + else { + lconf->thread_x = thread_generic; + } + } +} + +static void check_missing_rx(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + PROX_PANIC((targ->flags & TASK_ARG_RX_RING) && targ->rx_rings[0] == 0 && !targ->tx_opt_ring_task, + "Configuration Error - Core %u task %u Receiving from ring, but nobody xmitting to this ring\n", lconf->id, targ->id); + if (targ->nb_rxports == 0 && targ->nb_rxrings == 0) { + PROX_PANIC(!task_init_flag_set(targ->task_init, TASK_FEATURE_NO_RX), + "\tCore %u task %u: no rx_ports and no rx_rings configured while required by mode %s\n", lconf->id, targ->id, targ->task_init->mode_str); + } + } +} + +static void check_cfg_consistent(void) +{ + check_missing_rx(); + check_mixed_normal_pipeline(); +} + +static void plog_all_rings(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + for (uint8_t ring_idx = 0; ring_idx < targ->nb_rxrings; ++ring_idx) { + plog_info("\tCore %u, task %u, rx_ring[%u] %p\n", lconf->id, targ->id, ring_idx, targ->rx_rings[ring_idx]); + } + } +} + +static int chain_flag_state(struct task_args *targ, uint64_t flag, int is_set) +{ + if (task_init_flag_set(targ->task_init, flag) == is_set) + return 1; + + int ret = 0; + + for (uint32_t i = 0; i < targ->n_prev_tasks; ++i) { + ret = chain_flag_state(targ->prev_tasks[i], flag, is_set); + if (ret) + return 1; + } + return 0; +} + +static void configure_if_tx_queues(struct task_args *targ, uint8_t socket) +{ + uint8_t if_port; + + for (uint8_t i = 0; i < targ->nb_txports; ++i) { + if_port = targ->tx_port_queue[i].port; + + PROX_PANIC(if_port == OUT_DISCARD, "port misconfigured, exiting\n"); + + PROX_PANIC(!prox_port_cfg[if_port].active, "\tPort %u not used, skipping...\n", if_port); + + int dsocket = prox_port_cfg[if_port].socket; + if (dsocket != -1 && dsocket != socket) { + plog_warn("TX core on socket %d while device on socket %d\n", socket, dsocket); + } + + if (prox_port_cfg[if_port].tx_ring[0] == '\0') { // Rings-backed port can use single queue + targ->tx_port_queue[i].queue = prox_port_cfg[if_port].n_txq; + prox_port_cfg[if_port].n_txq++; + } else { + prox_port_cfg[if_port].n_txq = 1; + targ->tx_port_queue[i].queue = 0; + } + /* Set the ETH_TXQ_FLAGS_NOREFCOUNT flag if none of + the tasks up to the task transmitting to the port + does not use refcnt. */ + if (!chain_flag_state(targ, TASK_FEATURE_TXQ_FLAGS_REFCOUNT, 1)) { + prox_port_cfg[if_port].tx_conf.txq_flags |= ETH_TXQ_FLAGS_NOREFCOUNT; + plog_info("\t\tEnabling No refcnt on port %d\n", if_port); + } + else { + plog_info("\t\tRefcnt used on port %d\n", if_port); + } + + /* By default OFFLOAD is enabled, but if the whole + chain has NOOFFLOADS set all the way until the + first task that receives from a port, it will be + disabled for the destination port. */ + if (chain_flag_state(targ, TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, 1)) { + prox_port_cfg[if_port].tx_conf.txq_flags |= ETH_TXQ_FLAGS_NOOFFLOADS; + plog_info("\t\tDisabling TX offloads on port %d\n", if_port); + } else { + plog_info("\t\tEnabling TX offloads on port %d\n", if_port); + } + + /* By default NOMULTSEGS is disabled, as drivers/NIC might split packets on RX + It should only be enabled when we know for sure that the RX does not split packets. + Set the ETH_TXQ_FLAGS_NOMULTSEGS flag if none of the tasks up to the task + transmitting to the port does not use multsegs. */ + if (!chain_flag_state(targ, TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS, 0)) { + prox_port_cfg[if_port].tx_conf.txq_flags |= ETH_TXQ_FLAGS_NOMULTSEGS; + plog_info("\t\tEnabling No MultiSegs on port %d\n", if_port); + } + else { + plog_info("\t\tMultiSegs used on port %d\n", if_port); + } + } +} + +static void configure_if_rx_queues(struct task_args *targ, uint8_t socket) +{ + for (int i = 0; i < targ->nb_rxports; i++) { + uint8_t if_port = targ->rx_port_queue[i].port; + + if (if_port == OUT_DISCARD) { + return; + } + + PROX_PANIC(!prox_port_cfg[if_port].active, "Port %u not used, aborting...\n", if_port); + + if(prox_port_cfg[if_port].rx_ring[0] != '\0') { + prox_port_cfg[if_port].n_rxq = 0; + } + + targ->rx_port_queue[i].queue = prox_port_cfg[if_port].n_rxq; + prox_port_cfg[if_port].pool[targ->rx_port_queue[i].queue] = targ->pool; + prox_port_cfg[if_port].pool_size[targ->rx_port_queue[i].queue] = targ->nb_mbuf - 1; + prox_port_cfg[if_port].n_rxq++; + + int dsocket = prox_port_cfg[if_port].socket; + if (dsocket != -1 && dsocket != socket) { + plog_warn("RX core on socket %d while device on socket %d\n", socket, dsocket); + } + } +} + +static void configure_if_queues(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + uint8_t socket; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + socket = rte_lcore_to_socket_id(lconf->id); + + configure_if_tx_queues(targ, socket); + configure_if_rx_queues(targ, socket); + } +} + +static const char *gen_ring_name(void) +{ + static char retval[] = "XX"; + static const char* ring_names = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "[\\]^_`!\"#$%&'()*+,-./:;<=" + ">?@{|}0123456789"; + static int idx2 = 0; + + int idx = idx2; + + retval[0] = ring_names[idx % strlen(ring_names)]; + idx /= strlen(ring_names); + retval[1] = idx ? ring_names[(idx - 1) % strlen(ring_names)] : 0; + + idx2++; + + return retval; +} + +static int task_is_master(struct task_args *targ) +{ + return !targ->lconf; +} + +struct ring_init_stats { + uint32_t n_pkt_rings; + uint32_t n_ctrl_rings; + uint32_t n_opt_rings; +}; + +static uint32_t ring_init_stats_total(const struct ring_init_stats *ris) +{ + return ris->n_pkt_rings + ris->n_ctrl_rings + ris->n_opt_rings; +} + +static uint32_t count_incoming_tasks(uint32_t lcore_worker, uint32_t dest_task) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + uint32_t ret = 0; + struct core_task ct; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + for (uint8_t idxx = 0; idxx < MAX_PROTOCOLS; ++idxx) { + for (uint8_t ridx = 0; ridx < targ->core_task_set[idxx].n_elems; ++ridx) { + ct = targ->core_task_set[idxx].core_task[ridx]; + + if (dest_task == ct.task && lcore_worker == ct.core) + ret++; + } + } + } + return ret; +} + +static struct rte_ring *get_existing_ring(uint32_t lcore_id, uint32_t task_id) +{ + if (!prox_core_active(lcore_id, 0)) + return NULL; + + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (task_id >= lconf->n_tasks_all) + return NULL; + + if (lconf->targs[task_id].nb_rxrings == 0) + return NULL; + + return lconf->targs[task_id].rx_rings[0]; +} + +static void init_ring_between_tasks(struct lcore_cfg *lconf, struct task_args *starg, + const struct core_task ct, uint8_t ring_idx, int idx, + struct ring_init_stats *ris) +{ + uint8_t socket; + struct rte_ring *ring = NULL; + struct lcore_cfg *lworker; + struct task_args *dtarg; + + PROX_ASSERT(prox_core_active(ct.core, 0)); + lworker = &lcore_cfg[ct.core]; + + /* socket used is the one that the sending core resides on */ + socket = rte_lcore_to_socket_id(lconf->id); + + plog_info("\t\tCreating ring on socket %u with size %u\n" + "\t\t\tsource core, task and socket = %u, %u, %u\n" + "\t\t\tdestination core, task and socket = %u, %u, %u\n" + "\t\t\tdestination worker id = %u\n", + socket, starg->ring_size, + lconf->id, starg->id, socket, + ct.core, ct.task, rte_lcore_to_socket_id(ct.core), + ring_idx); + + if (ct.type) { + struct rte_ring **dring = NULL; + + if (ct.type == CTRL_TYPE_MSG) + dring = &lworker->ctrl_rings_m[ct.task]; + else if (ct.type == CTRL_TYPE_PKT) { + dring = &lworker->ctrl_rings_p[ct.task]; + starg->flags |= TASK_ARG_CTRL_RINGS_P; + } + + if (*dring == NULL) + ring = rte_ring_create(gen_ring_name(), starg->ring_size, socket, RING_F_SC_DEQ); + else + ring = *dring; + PROX_PANIC(ring == NULL, "Cannot create ring to connect I/O core %u with worker core %u\n", lconf->id, ct.core); + + starg->tx_rings[starg->tot_n_txrings_inited] = ring; + starg->tot_n_txrings_inited++; + *dring = ring; + if (lconf->id == prox_cfg.master) { + ctrl_rings[ct.core*MAX_TASKS_PER_CORE + ct.task] = ring; + } + + plog_info("\t\tCore %u task %u to -> core %u task %u ctrl_ring %s %p %s\n", + lconf->id, starg->id, ct.core, ct.task, ct.type == CTRL_TYPE_PKT? + "pkt" : "msg", ring, ring->name); + ris->n_ctrl_rings++; + return; + } + + dtarg = &lworker->targs[ct.task]; + lworker->targs[ct.task].worker_thread_id = ring_idx; + PROX_ASSERT(dtarg->flags & TASK_ARG_RX_RING); + PROX_ASSERT(ct.task < lworker->n_tasks_all); + + /* If all the following conditions are met, the ring can be + optimized away. */ + if (!task_is_master(starg) && starg->lconf->id == dtarg->lconf->id && + starg->nb_txrings == 1 && idx == 0 && dtarg->task && + dtarg->tot_rxrings == 1 && starg->task == dtarg->task - 1) { + plog_info("\t\tOptimizing away ring on core %u from task %u to task %u\n", + dtarg->lconf->id, starg->task, dtarg->task); + /* No need to set up ws_mbuf. */ + starg->tx_opt_ring = 1; + /* During init of destination task, the buffer in the + source task will be initialized. */ + dtarg->tx_opt_ring_task = starg; + ris->n_opt_rings++; + ++dtarg->nb_rxrings; + return; + } + + int ring_created = 1; + /* Only create multi-producer rings if configured to do so AND + there is only one task sending to the task */ + if ((prox_cfg.flags & DSF_MP_RINGS && count_incoming_tasks(ct.core, ct.task) > 1) + || (prox_cfg.flags & DSF_ENABLE_BYPASS)) { + ring = get_existing_ring(ct.core, ct.task); + + if (ring) { + plog_info("\t\tCore %u task %u creatign MP ring %p to core %u task %u\n", + lconf->id, starg->id, ring, ct.core, ct.task); + ring_created = 0; + } + else { + ring = rte_ring_create(gen_ring_name(), starg->ring_size, socket, RING_F_SC_DEQ); + plog_info("\t\tCore %u task %u using MP ring %p from core %u task %u\n", + lconf->id, starg->id, ring, ct.core, ct.task); + } + } + else + ring = rte_ring_create(gen_ring_name(), starg->ring_size, socket, RING_F_SP_ENQ | RING_F_SC_DEQ); + + PROX_PANIC(ring == NULL, "Cannot create ring to connect I/O core %u with worker core %u\n", lconf->id, ct.core); + + starg->tx_rings[starg->tot_n_txrings_inited] = ring; + starg->tot_n_txrings_inited++; + + if (ring_created) { + PROX_ASSERT(dtarg->nb_rxrings < MAX_RINGS_PER_TASK); + dtarg->rx_rings[dtarg->nb_rxrings] = ring; + ++dtarg->nb_rxrings; + } + dtarg->nb_slave_threads = starg->core_task_set[idx].n_elems; + dtarg->lb_friend_core = lconf->id; + dtarg->lb_friend_task = starg->id; + plog_info("\t\tWorker thread %d has core %d, task %d as a lb friend\n", ct.core, lconf->id, starg->id); + plog_info("\t\tCore %u task %u tx_ring[%u] -> core %u task %u rx_ring[%u] %p %s %u WT\n", + lconf->id, starg->id, ring_idx, ct.core, ct.task, dtarg->nb_rxrings, ring, ring->name, + dtarg->nb_slave_threads); + ++ris->n_pkt_rings; +} + +static void init_rings(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *starg; + struct ring_init_stats ris = {0}; + + while (core_targ_next(&lconf, &starg, 1) == 0) { + plog_info("\t*** Initializing rings on core %u, task %u ***\n", lconf->id, starg->id); + for (uint8_t idx = 0; idx < MAX_PROTOCOLS; ++idx) { + for (uint8_t ring_idx = 0; ring_idx < starg->core_task_set[idx].n_elems; ++ring_idx) { + PROX_ASSERT(ring_idx < MAX_WT_PER_LB); + PROX_ASSERT(starg->tot_n_txrings_inited < MAX_RINGS_PER_TASK); + + struct core_task ct = starg->core_task_set[idx].core_task[ring_idx]; + init_ring_between_tasks(lconf, starg, ct, ring_idx, idx, &ris); + } + } + } + + plog_info("\tInitialized %d rings:\n" + "\t\tNumber of packet rings: %u\n" + "\t\tNumber of control rings: %u\n" + "\t\tNumber of optimized rings: %u\n", + ring_init_stats_total(&ris), + ris.n_pkt_rings, + ris.n_ctrl_rings, + ris.n_opt_rings); +} + +static void shuffle_mempool(struct rte_mempool* mempool, uint32_t nb_mbuf) +{ + struct rte_mbuf** pkts = prox_zmalloc(nb_mbuf * sizeof(*pkts), rte_socket_id()); + uint64_t got = 0; + + while (rte_mempool_get_bulk(mempool, (void**)(pkts + got), 1) == 0) + ++got; + + while (got) { + int idx; + do { + idx = rand() % nb_mbuf - 1; + } while (pkts[idx] == 0); + + rte_mempool_put_bulk(mempool, (void**)&pkts[idx], 1); + pkts[idx] = 0; + --got; + }; + prox_free(pkts); +} + +static void setup_mempools_unique_per_socket(void) +{ + uint32_t flags = 0; + char name[64]; + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + struct rte_mempool *pool[MAX_SOCKETS]; + uint32_t mbuf_count[MAX_SOCKETS] = {0}; + uint32_t nb_cache_mbuf[MAX_SOCKETS] = {0}; + uint32_t mbuf_size[MAX_SOCKETS] = {0}; + + while (core_targ_next_early(&lconf, &targ, 0) == 0) { + PROX_PANIC(targ->task_init == NULL, "task_init = NULL, is mode specified for core %d, task %d ?\n", lconf->id, targ->id); + uint8_t socket = rte_lcore_to_socket_id(lconf->id); + PROX_ASSERT(socket < MAX_SOCKETS); + + if (targ->mbuf_size_set_explicitely) + flags = MEMPOOL_F_NO_SPREAD; + if ((!targ->mbuf_size_set_explicitely) && (targ->task_init->mbuf_size != 0)) { + targ->mbuf_size = targ->task_init->mbuf_size; + } + if (targ->rx_port_queue[0].port != OUT_DISCARD) { + struct prox_port_cfg* port_cfg = &prox_port_cfg[targ->rx_port_queue[0].port]; + PROX_ASSERT(targ->nb_mbuf != 0); + mbuf_count[socket] += targ->nb_mbuf; + if (nb_cache_mbuf[socket] == 0) + nb_cache_mbuf[socket] = targ->nb_cache_mbuf; + else { + PROX_PANIC(nb_cache_mbuf[socket] != targ->nb_cache_mbuf, + "all mbuf_cache must have the same size if using a unique mempool per socket\n"); + } + if (mbuf_size[socket] == 0) + mbuf_size[socket] = targ->mbuf_size; + else { + PROX_PANIC(mbuf_size[socket] != targ->mbuf_size, + "all mbuf_size must have the same size if using a unique mempool per socket\n"); + } + if ((!targ->mbuf_size_set_explicitely) && (strcmp(port_cfg->short_name, "vmxnet3") == 0)) { + if (mbuf_size[socket] < MBUF_SIZE + RTE_PKTMBUF_HEADROOM) + mbuf_size[socket] = MBUF_SIZE + RTE_PKTMBUF_HEADROOM; + } + } + } + for (int i = 0 ; i < MAX_SOCKETS; i++) { + if (mbuf_count[i] != 0) { + sprintf(name, "socket_%u_pool", i); + pool[i] = rte_mempool_create(name, + mbuf_count[i] - 1, mbuf_size[i], + nb_cache_mbuf[i], + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + prox_pktmbuf_init, NULL, + i, flags); + PROX_PANIC(pool[i] == NULL, "\t\tError: cannot create mempool for socket %u\n", i); + plog_info("\t\tMempool %p size = %u * %u cache %u, socket %d\n", pool[i], + mbuf_count[i], mbuf_size[i], nb_cache_mbuf[i], i); + + if (prox_cfg.flags & DSF_SHUFFLE) { + shuffle_mempool(pool[i], mbuf_count[i]); + } + } + } + + lconf = NULL; + while (core_targ_next_early(&lconf, &targ, 0) == 0) { + uint8_t socket = rte_lcore_to_socket_id(lconf->id); + + if (targ->rx_port_queue[0].port != OUT_DISCARD) { + /* use this pool for the interface that the core is receiving from */ + /* If one core receives from multiple ports, all the ports use the same mempool */ + targ->pool = pool[socket]; + /* Set the number of mbuf to the number of the unique mempool, so that the used and free work */ + targ->nb_mbuf = mbuf_count[socket]; + plog_info("\t\tMempool %p size = %u * %u cache %u, socket %d\n", targ->pool, + targ->nb_mbuf, mbuf_size[socket], targ->nb_cache_mbuf, socket); + } + } +} + +static void setup_mempool_for_rx_task(struct lcore_cfg *lconf, struct task_args *targ) +{ + const uint8_t socket = rte_lcore_to_socket_id(lconf->id); + struct prox_port_cfg *port_cfg = &prox_port_cfg[targ->rx_port_queue[0].port]; + const struct rte_memzone *mz; + struct rte_mempool *mp = NULL; + uint32_t flags = 0; + char memzone_name[64]; + char name[64]; + + /* mbuf size can be set + * - from config file (highest priority, overwriting any other config) - should only be used as workaround + * - through each 'mode', overwriting the default mbuf_size + * - defaulted to MBUF_SIZE i.e. 1518 Bytes + * Except is set expliciteky, ensure that size is big enough for vmxnet3 driver + */ + if (targ->mbuf_size_set_explicitely) { + flags = MEMPOOL_F_NO_SPREAD; + /* targ->mbuf_size already set */ + } + else if (targ->task_init->mbuf_size != 0) { + /* mbuf_size not set through config file but set through mode */ + targ->mbuf_size = targ->task_init->mbuf_size; + } + else if (strcmp(port_cfg->short_name, "vmxnet3") == 0) { + if (targ->mbuf_size < MBUF_SIZE + RTE_PKTMBUF_HEADROOM) + targ->mbuf_size = MBUF_SIZE + RTE_PKTMBUF_HEADROOM; + } + + /* allocate memory pool for packets */ + PROX_ASSERT(targ->nb_mbuf != 0); + + if (targ->pool_name[0] == '\0') { + sprintf(name, "core_%u_port_%u_pool", lconf->id, targ->id); + } + + snprintf(memzone_name, sizeof(memzone_name)-1, "MP_%s", targ->pool_name); + mz = rte_memzone_lookup(memzone_name); + + if (mz != NULL) { + mp = (struct rte_mempool*)mz->addr; + + targ->nb_mbuf = mp->size; + targ->pool = mp; + } + +#ifdef RTE_LIBRTE_IVSHMEM_FALSE + if (mz != NULL && mp != NULL && mp->phys_addr != mz->ioremap_addr) { + /* Init mbufs with ioremap_addr for dma */ + mp->phys_addr = mz->ioremap_addr; + mp->elt_pa[0] = mp->phys_addr + (mp->elt_va_start - (uintptr_t)mp); + + struct prox_pktmbuf_reinit_args init_args; + init_args.mp = mp; + init_args.lconf = lconf; + + uint32_t elt_sz = mp->elt_size + mp->header_size + mp->trailer_size; + rte_mempool_obj_iter((void*)mp->elt_va_start, mp->size, elt_sz, 1, + mp->elt_pa, mp->pg_num, mp->pg_shift, prox_pktmbuf_reinit, &init_args); + } +#endif + + /* Use this pool for the interface that the core is + receiving from if one core receives from multiple + ports, all the ports use the same mempool */ + if (targ->pool == NULL) { + plog_info("\t\tCreating mempool with name '%s'\n", name); + targ->pool = rte_mempool_create(name, + targ->nb_mbuf - 1, targ->mbuf_size, + targ->nb_cache_mbuf, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + prox_pktmbuf_init, lconf, + socket, flags); + } + + PROX_PANIC(targ->pool == NULL, + "\t\tError: cannot create mempool for core %u port %u: %s\n", lconf->id, targ->id, rte_strerror(rte_errno)); + + plog_info("\t\tMempool %p size = %u * %u cache %u, socket %d\n", targ->pool, + targ->nb_mbuf, targ->mbuf_size, targ->nb_cache_mbuf, socket); + if (prox_cfg.flags & DSF_SHUFFLE) { + shuffle_mempool(targ->pool, targ->nb_mbuf); + } +} + +static void setup_mempools_multiple_per_socket(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + while (core_targ_next_early(&lconf, &targ, 0) == 0) { + PROX_PANIC(targ->task_init == NULL, "task_init = NULL, is mode specified for core %d, task %d ?\n", lconf->id, targ->id); + if (targ->rx_port_queue[0].port == OUT_DISCARD) + continue; + setup_mempool_for_rx_task(lconf, targ); + } +} + +static void setup_mempools(void) +{ + if (prox_cfg.flags & UNIQUE_MEMPOOL_PER_SOCKET) + setup_mempools_unique_per_socket(); + else + setup_mempools_multiple_per_socket(); +} + +static void set_task_lconf(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + lconf->targs[task_id].lconf = lconf; + } + } +} + +static void set_dest_threads(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + while (core_targ_next(&lconf, &targ, 0) == 0) { + for (uint8_t idx = 0; idx < MAX_PROTOCOLS; ++idx) { + for (uint8_t ring_idx = 0; ring_idx < targ->core_task_set[idx].n_elems; ++ring_idx) { + struct core_task ct = targ->core_task_set[idx].core_task[ring_idx]; + + struct task_args *dest_task = core_targ_get(ct.core, ct.task); + dest_task->prev_tasks[dest_task->n_prev_tasks++] = targ; + } + } + } +} + +static void setup_all_task_structs_early_init(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + plog_info("\t*** Calling early init on all tasks ***\n"); + while (core_targ_next(&lconf, &targ, 0) == 0) { + if (targ->task_init->early_init) { + targ->task_init->early_init(targ); + } + } +} + +static void setup_all_task_structs(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + lconf->tasks_all[task_id] = init_task_struct(&lconf->targs[task_id]); + } + } +} + +static void init_port_activate(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + uint8_t port_id = 0; + + while (core_targ_next_early(&lconf, &targ, 0) == 0) { + for (int i = 0; i < targ->nb_rxports; i++) { + port_id = targ->rx_port_queue[i].port; + prox_port_cfg[port_id].active = 1; + } + + for (int i = 0; i < targ->nb_txports; i++) { + port_id = targ->tx_port_queue[i].port; + prox_port_cfg[port_id].active = 1; + } + } +} + +/* Initialize cores and allocate mempools */ +static void init_lcores(void) +{ + struct lcore_cfg *lconf = 0; + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + uint8_t socket = rte_lcore_to_socket_id(lcore_id); + PROX_PANIC(socket + 1 > MAX_SOCKETS, "Can't configure core %u (on socket %u). MAX_SOCKET is set to %d\n", lcore_id, socket, MAX_SOCKETS); + } + + /* need to allocate mempools as the first thing to use the lowest possible address range */ + plog_info("=== Initializing mempools ===\n"); + setup_mempools(); + + lcore_cfg_alloc_hp(); + + set_dest_threads(); + set_task_lconf(); + + plog_info("=== Initializing port addresses ===\n"); + init_port_addr(); + + plog_info("=== Initializing queue numbers on cores ===\n"); + configure_if_queues(); + + plog_info("=== Initializing rings on cores ===\n"); + init_rings(); + + plog_info("=== Checking configuration consistency ===\n"); + check_cfg_consistent(); + + plog_all_rings(); + + setup_all_task_structs_early_init(); + plog_info("=== Initializing tasks ===\n"); + setup_all_task_structs(); +} + +static int setup_prox(int argc, char **argv) +{ + if (prox_read_config_file() != 0 || + prox_setup_rte(argv[0]) != 0) { + return -1; + } + + if (prox_cfg.flags & DSF_CHECK_SYNTAX) { + plog_info("=== Configuration file syntax has been checked ===\n\n"); + exit(EXIT_SUCCESS); + } + + init_port_activate(); + plog_info("=== Initializing rte devices ===\n"); + if (!(prox_cfg.flags & DSF_USE_DUMMY_DEVICES)) + init_rte_ring_dev(); + init_rte_dev(prox_cfg.flags & DSF_USE_DUMMY_DEVICES); + plog_info("=== Calibrating TSC overhead ===\n"); + clock_init(); + plog_info("\tTSC running at %"PRIu64" Hz\n", rte_get_tsc_hz()); + + init_lcores(); + plog_info("=== Initializing ports ===\n"); + init_port_all(); + + if (prox_cfg.logbuf_size) { + prox_cfg.logbuf = prox_zmalloc(prox_cfg.logbuf_size, rte_socket_id()); + PROX_PANIC(prox_cfg.logbuf == NULL, "Failed to allocate memory for logbuf with size = %d\n", prox_cfg.logbuf_size); + } + + if (prox_cfg.flags & DSF_CHECK_INIT) { + plog_info("=== Initialization sequence completed ===\n\n"); + exit(EXIT_SUCCESS); + } + + /* Current way that works to disable DPDK logging */ + FILE *f = fopen("/dev/null", "r"); + rte_openlog_stream(f); + plog_info("=== PROX started ===\n"); + return 0; +} + +static int success = 0; +static void siguser_handler(int signal) +{ + if (signal == SIGUSR1) + success = 1; + else + success = 0; +} + +static void sigabrt_handler(__attribute__((unused)) int signum) +{ + /* restore default disposition for SIGABRT and SIGPIPE */ + signal(SIGABRT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + /* ignore further Ctrl-C */ + signal(SIGINT, SIG_IGN); + + /* more drastic exit on tedious termination signal */ + plog_info("Aborting...\n"); + if (lcore_cfg != NULL) { + uint32_t lcore_id; + pthread_t thread_id, tid0, tid = pthread_self(); + memset(&tid0, 0, sizeof(tid0)); + + /* cancel all threads except current one */ + lcore_id = -1; + while (prox_core_next(&lcore_id, 1) == 0) { + thread_id = lcore_cfg[lcore_id].thread_id; + if (pthread_equal(thread_id, tid0)) + continue; + if (pthread_equal(thread_id, tid)) + continue; + pthread_cancel(thread_id); + } + + /* wait for cancelled threads to terminate */ + lcore_id = -1; + while (prox_core_next(&lcore_id, 1) == 0) { + thread_id = lcore_cfg[lcore_id].thread_id; + if (pthread_equal(thread_id, tid0)) + continue; + if (pthread_equal(thread_id, tid)) + continue; + pthread_join(thread_id, NULL); + } + } + + /* close ncurses */ + display_end(); + + /* close ports on termination signal */ + close_ports_atexit(); + + /* terminate now */ + abort(); +} + +static void sigterm_handler(int signum) +{ + /* abort on second Ctrl-C */ + if (signum == SIGINT) + signal(SIGINT, sigabrt_handler); + + /* gracefully quit on harmless termination signal */ + /* ports will subsequently get closed at resulting exit */ + quit(); +} + +int main(int argc, char **argv) +{ + /* set en_US locale to print big numbers with ',' */ + setlocale(LC_NUMERIC, "en_US.utf-8"); + + if (prox_parse_args(argc, argv) != 0){ + prox_usage(argv[0]); + } + + plog_init(prox_cfg.log_name, prox_cfg.log_name_pid); + plog_info("=== " PROGRAM_NAME " " VERSION_STR " ===\n"); + plog_info("\tUsing DPDK %s\n", rte_version() + sizeof(RTE_VER_PREFIX)); + read_rdt_info(); + + if (prox_cfg.flags & DSF_LIST_TASK_MODES) { + /* list supported task modes and exit */ + tasks_list(); + return EXIT_SUCCESS; + } + + /* close ports at normal exit */ + atexit(close_ports_atexit); + /* gracefully quit on harmless termination signals */ + signal(SIGHUP, sigterm_handler); + signal(SIGINT, sigterm_handler); + signal(SIGQUIT, sigterm_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGUSR1, sigterm_handler); + signal(SIGUSR2, sigterm_handler); + /* more drastic exit on tedious termination signals */ + signal(SIGABRT, sigabrt_handler); + signal(SIGPIPE, sigabrt_handler); + + if (prox_cfg.flags & DSF_DAEMON) { + signal(SIGUSR1, siguser_handler); + signal(SIGUSR2, siguser_handler); + plog_info("=== Running in Daemon mode ===\n"); + plog_info("\tForking child and waiting for setup completion\n"); + + pid_t ppid = getpid(); + pid_t pid = fork(); + if (pid < 0) { + plog_err("Failed to fork process to run in daemon mode\n"); + return EXIT_FAILURE; + } + + if (pid == 0) { + fclose(stdin); + fclose(stdout); + fclose(stderr); + if (setsid() < 0) { + kill(ppid, SIGUSR2); + return EXIT_FAILURE; + } + if (setup_prox(argc, argv) != 0) { + kill(ppid, SIGUSR2); + return EXIT_FAILURE; + } + else { + kill(ppid, SIGUSR1); + run(prox_cfg.flags); + return EXIT_SUCCESS; + } + } + else { + /* Before exiting the parent, wait until the + child process has finished setting up */ + pause(); + if (prox_cfg.logbuf) { + file_print(prox_cfg.logbuf); + } + return success? EXIT_SUCCESS : EXIT_FAILURE; + } + } + + if (setup_prox(argc, argv) != 0) + return EXIT_FAILURE; + run(prox_cfg.flags); + return EXIT_SUCCESS; +} diff --git a/VNFs/DPPD-PROX/main.h b/VNFs/DPPD-PROX/main.h new file mode 100644 index 00000000..5daef700 --- /dev/null +++ b/VNFs/DPPD-PROX/main.h @@ -0,0 +1,41 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include <rte_version.h> +#include "hash_entry_types.h" +#ifdef RTE_EXEC_ENV_BAREMETAL +#error A linuxapp configuration target is required! +#endif + +#if RTE_VERSION < RTE_VERSION_NUM(1,7,0,0) +#error At least Intel(R) DPDK version 1.7.0 is required +#endif + +#ifndef __INTEL_COMPILER +#if __GNUC__ == 4 && __GNUC_MINOR__ < 7 +#error Only GCC versions 4.7 and above supported +#endif +#endif + +struct rte_ring; +// in main.c +extern uint8_t port_status[]; +extern struct rte_ring *ctrl_rings[]; + +#endif /* _MAIN_H_ */ diff --git a/VNFs/DPPD-PROX/mbuf_utils.h b/VNFs/DPPD-PROX/mbuf_utils.h new file mode 100644 index 00000000..22d57a39 --- /dev/null +++ b/VNFs/DPPD-PROX/mbuf_utils.h @@ -0,0 +1,57 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _MBUF_UTILS_H_ +#define _MBUF_UTILS_H_ + +#include <string.h> + +#include <rte_ip.h> +#include <rte_version.h> +#include <rte_ether.h> + +static void init_mbuf_seg(struct rte_mbuf *mbuf) +{ +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + mbuf->nb_segs = 1; +#else + mbuf->pkt.nb_segs = 1; +#endif + rte_mbuf_refcnt_set(mbuf, 1); +} + +static uint16_t pkt_len_to_wire_size(uint16_t pkt_len) +{ + return (pkt_len < 60? 60 : pkt_len) + ETHER_CRC_LEN + 20; +} + +static uint16_t mbuf_wire_size(const struct rte_mbuf *mbuf) +{ + uint16_t pkt_len = rte_pktmbuf_pkt_len(mbuf); + + return pkt_len_to_wire_size(pkt_len); +} + +static uint16_t mbuf_calc_padlen(const struct rte_mbuf *mbuf, void *pkt, struct ipv4_hdr *ipv4) +{ + uint16_t pkt_len = rte_pktmbuf_pkt_len(mbuf); + uint16_t ip_offset = (uint8_t *)ipv4 - (uint8_t*)pkt; + uint16_t ip_total_len = rte_be_to_cpu_16(ipv4->total_length); + + return pkt_len - ip_total_len - ip_offset; +} + +#endif /* _MBUF_UTILS_H_ */ diff --git a/VNFs/DPPD-PROX/mpls.h b/VNFs/DPPD-PROX/mpls.h new file mode 100644 index 00000000..93f3e3d5 --- /dev/null +++ b/VNFs/DPPD-PROX/mpls.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _MPLS_H_ +#define _MPLS_H_ + +struct mpls_hdr { + union { + struct { + uint16_t lbl_h; /* Label */ + uint8_t bos: 1; /* Bottom of Stack */ + uint8_t cos: 3; /* Class of Service */ + uint8_t lbl_l: 4; /* Label */ + uint8_t ttl; /* Time to Live, 64 */ + }; + uint32_t bytes; + }; +} __attribute__((__packed__)); + +#endif /* _MPLS_H_ */ diff --git a/VNFs/DPPD-PROX/msr.c b/VNFs/DPPD-PROX/msr.c new file mode 100644 index 00000000..194d4c75 --- /dev/null +++ b/VNFs/DPPD-PROX/msr.c @@ -0,0 +1,80 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> + +#include "msr.h" +#include "log.h" + +int msr_fd[RTE_MAX_LCORE]; +int n_msr_fd; +int msr_init(void) +{ + char msr_path[1024]; + + if (n_msr_fd) { + return 0; + } + + for (uint32_t i = 0; i < sizeof(msr_fd)/sizeof(*msr_fd); ++i, n_msr_fd = i) { + snprintf(msr_path, sizeof(msr_path), "/dev/cpu/%u/msr", i); + msr_fd[i] = open(msr_path, O_RDWR); + if (msr_fd[i] < 0) { + return i == 0? -1 : 0; + } + } + + return 0; +} + +void msr_cleanup(void) +{ + for (int i = 0; i < n_msr_fd; ++i) { + close(msr_fd[i]); + } + + n_msr_fd = 0; +} + +int msr_read(uint64_t *ret, int lcore_id, off_t offset) +{ + if (lcore_id > n_msr_fd) { + return -1; + } + + if (0 > pread(msr_fd[lcore_id], ret, sizeof(uint64_t), offset)) { + return -1; + } + + return 0; +} + +int msr_write(int lcore_id, uint64_t val, off_t offset) +{ + if (lcore_id > n_msr_fd) { + return -1; + } + + if (sizeof(uint64_t) != pwrite(msr_fd[lcore_id], &val, sizeof(uint64_t), offset)) { + return -1; + } + plog_dbg("\t\tmsr_write(core %d, offset %x, val %lx)\n", lcore_id, (int)offset, val); + return 0; +} diff --git a/VNFs/DPPD-PROX/msr.h b/VNFs/DPPD-PROX/msr.h new file mode 100644 index 00000000..a8a46c86 --- /dev/null +++ b/VNFs/DPPD-PROX/msr.h @@ -0,0 +1,24 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <fcntl.h> + +int msr_init(void); +void msr_cleanup(void); + +int msr_read(uint64_t *ret, int lcore_id, off_t offset); +int msr_write(int lcore_id, uint64_t val, off_t offset); diff --git a/VNFs/DPPD-PROX/parse_utils.c b/VNFs/DPPD-PROX/parse_utils.c new file mode 100644 index 00000000..d258c591 --- /dev/null +++ b/VNFs/DPPD-PROX/parse_utils.c @@ -0,0 +1,1420 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <ctype.h> +#include <stdio.h> +#include <float.h> +#include <math.h> +#include <unistd.h> +#include <errno.h> +#include <stdarg.h> + +#include <rte_ether.h> +#include <rte_string_fns.h> + +#include "quit.h" +#include "cfgfile.h" +#include "ip6_addr.h" +#include "parse_utils.h" +#include "prox_globals.h" +#include "prox_cfg.h" +#include "log.h" +#include "prox_lua.h" +#include "prox_lua_types.h" + +#define MAX_NB_PORT_NAMES PROX_MAX_PORTS +#define MAX_LEN_PORT_NAME 24 +#define MAX_LEN_VAR_NAME 24 +#define MAX_LEN_VAL 512 +#define MAX_NB_VARS 32 + +#if MAX_WT_PER_LB > MAX_INDEX +#error MAX_WT_PER_LB > MAX_INDEX +#endif + +/* The CPU topology of the system is used to parse "socket + notation". This notation allows to refer to cores on specific + sockets and the hyper-thread of those cores. The CPU topology is + loaded only if the socket notation is used at least once. */ + +struct cpu_topology { + int socket[MAX_SOCKETS][RTE_MAX_LCORE][2]; + uint32_t n_cores[MAX_SOCKETS]; + uint32_t n_sockets; +}; + +struct cpu_topology cpu_topo; + +struct port_name { + uint32_t id; + char name[MAX_LEN_PORT_NAME]; +}; + +static struct port_name port_names[MAX_NB_PORT_NAMES]; +static uint8_t nb_port_names; + +struct var { + uint8_t cli; + char name[MAX_LEN_VAR_NAME]; + char val[MAX_LEN_VAL]; +}; + +static struct var vars[MAX_NB_VARS]; +static uint8_t nb_vars; + +static char format_err_str[256]; +static const char *err_str = ""; + +const char *get_parse_err(void) +{ + return err_str; +} + +static int read_cpu_topology(void); + +static int parse_core(int *socket, int *core, int *ht, const char* str); + +static void set_errf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vsnprintf(format_err_str, sizeof(format_err_str), format, ap); + va_end(ap); + err_str = format_err_str; +} + +static struct var *var_lookup(const char *name) +{ + for (uint8_t i = 0; i < nb_vars; ++i) { + if (!strcmp(name, vars[i].name)) { + return &vars[i]; + } + } + return NULL; +} + +static int parse_single_var(char *val, size_t len, const char *name) +{ + struct var *match; + + match = var_lookup(name); + if (match) { + if (strlen(match->val) + 1 > len) { + set_errf("Variables '%s' with value '%s' is too long\n", + match->name, match->val); + return -1; + } + strncpy(val, match->val, len); + return 0; + } + else { + /* name + 1 to skip leading '$' */ + if (lua_to_string(prox_lua(), GLOBAL, name + 1, val, len) >= 0) + return 0; + } + + set_errf("Variable '%s' not defined!", name); + return 1; +} + +/* Replace $... and each occurrence of ${...} with variable values */ +int parse_vars(char *val, size_t len, const char *name) +{ + static char result[MAX_CFG_STRING_LEN]; + static char cur_var[MAX_CFG_STRING_LEN]; + char parsed[2048]; + size_t name_len = strlen(name); + enum parse_vars_state {NO_VAR, WHOLE_VAR, INLINE_VAR} state = NO_VAR; + size_t result_len = 0; + size_t start_var = 0; + + memset(result, 0, sizeof(result)); + PROX_PANIC(name_len > sizeof(result), "\tUnable to parse var %s: too long\n", name); + + for (size_t i = 0; i < name_len; ++i) { + switch (state) { + case NO_VAR: + if (name[i] == '$') { + if (i != name_len - 1 && name[i + 1] == '{') { + start_var = i + 2; + state = INLINE_VAR; + i = i + 1; + } + else if (i == 0 && i != name_len - 1) { + state = WHOLE_VAR; + } + else { + set_errf("Invalid variable syntax"); + return -1; + } + } + else { + result[result_len++] = name[i]; + } + break; + case INLINE_VAR: + if (name[i] == '}') { + cur_var[0] = '$'; + size_t var_len = i - start_var; + if (var_len == 0) { + set_errf("Empty variable are not allowed"); + return -1; + } + + strncpy(&cur_var[1], &name[start_var], var_len); + cur_var[1 + var_len] = 0; + if (parse_single_var(parsed, sizeof(parsed), cur_var)) { + return -1; + } + strcpy(&result[result_len], parsed); + result_len += strlen(parsed); + state = NO_VAR; + } + else if (i == name_len - 1) { + set_errf("Invalid variable syntax, expected '}'."); + return -1; + } + break; + case WHOLE_VAR: + if (i == name_len - 1) { + return parse_single_var(val, len, name); + } + break; + } + } + strncpy(val, result, len); + + return 0; +} + +int parse_int_mask(uint32_t *val, uint32_t *mask, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *mask_str; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + mask_str = strchr(str, '&'); + + if (mask_str == NULL) { + set_errf("Missing '&' when parsing mask"); + return -2; + } + + *mask_str = 0; + + if (parse_int(val, str)) + return -1; + if (parse_int(mask, mask_str + 1)) + return -1; + + return 0; +} + +int parse_range(uint32_t* lo, uint32_t* hi, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *dash; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + dash = strstr(str, "-"); + + if (dash == NULL) { + set_errf("Missing '-' when parsing mask"); + return -2; + } + + *dash = 0; + + if (parse_int(lo, str)) + return -1; + if (parse_int(hi, dash + 1)) + return -1; + + int64_t tmp = strtol(str, 0, 0); + if (tmp > UINT32_MAX) { + set_errf("Integer is bigger than %u", UINT32_MAX); + return -1; + } + if (tmp < 0) { + set_errf("Integer is negative"); + return -2; + } + + *lo = tmp; + + tmp = strtol(dash + 1, 0, 0); + if (tmp > UINT32_MAX) { + set_errf("Integer is bigger than %u", UINT32_MAX); + return -1; + } + if (tmp < 0) { + set_errf("Integer is negative"); + return -2; + } + + *hi = tmp; + + if (*lo > *hi) { + set_errf("Low boundary is above high boundary in range"); + return -2; + } + + return 0; +} + +int parse_ip(uint32_t *addr, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + char *ip_parts[5]; + + if (strlen(str) > MAX_STR_LEN_PROC) { + set_errf("String too long (max supported: %d)", MAX_STR_LEN_PROC); + return -2; + } + + if (4 != rte_strsplit(str, strlen(str), ip_parts, 5, '.')) { + set_errf("Expecting 4 octets in ip."); + return -1; + } + + uint32_t val; + for (uint8_t i = 0; i < 4; ++i) { + val = atoi(ip_parts[i]); + if (val > 255) { + set_errf("Maximum value for octet is 255 but octet %u is %u", i, val); + return -1; + } + *addr = *addr << 8 | val; + } + return 0; +} + +int parse_ip4_cidr(struct ip4_subnet *val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *slash; + int prefix; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + slash = strstr(str, "/"); + + if (slash == NULL) { + set_errf("Missing '/' when parsing CIDR notation"); + return -2; + } + + *slash = 0; + prefix = atoi(slash + 1); + val->prefix = prefix; + + if (prefix > 32) { + set_errf("Prefix %d is too big", prefix); + return -2; + } + if (prefix < 1) { + set_errf("Prefix %d is too small", prefix); + } + if (parse_ip(&val->ip, str)) + return -2; + + /* Apply mask making all bits outside the prefix zero */ + val->ip &= ((int)(1 << 31)) >> (prefix - 1); + + return 0; +} + +int parse_ip6_cidr(struct ip6_subnet *val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *slash; + int prefix; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + slash = strstr(str, "/"); + + if (slash == NULL) { + set_errf("Missing '/' when parsing CIDR notation"); + return -2; + } + + *slash = 0; + prefix = atoi(slash + 1); + val->prefix = prefix; + + parse_ip6((struct ipv6_addr *)&val->ip, str); + + /* Apply mask making all bits outside the prefix zero */ + + int p = 120; + int cnt = 0; + + while (p >= prefix) { + val->ip[15-cnt] = 0; + p -= 8; + cnt++; + } + + if (prefix % 8 != 0) { + val->ip[15-cnt] &= ((int8_t)(1 << 7)) >> ((prefix %8) - 1); + } + + return 0; +} + +int parse_ip6(struct ipv6_addr *addr, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *addr_parts[9]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + uint8_t ret = rte_strsplit(str, strlen(str), addr_parts, 9, ':'); + + if (ret == 9) { + set_errf("Invalid IPv6 address"); + return -1; + } + + uint8_t omitted = 0; + + for (uint8_t i = 0, j = 0; i < ret; ++i, ++j) { + if (*addr_parts[i] == 0) { + if (omitted == 0) { + set_errf("Can only omit zeros once"); + return -1; + } + omitted = 1; + j += 8 - ret; + } + else { + uint16_t w = strtoll(addr_parts[i], NULL, 16); + addr->bytes[j++] = (w >> 8) & 0xff; + addr->bytes[j] = w & 0xff; + } + } + return 0; +} + +int parse_mac(struct ether_addr *ether_addr, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + char *addr_parts[7]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + uint8_t ret = rte_strsplit(str, strlen(str), addr_parts, 7, ':'); + + if (ret != 6) { + set_errf("Invalid MAC address format"); + return -1; + } + + for (uint8_t i = 0; i < 6; ++i) { + if (2 != strlen(addr_parts[i])) { + set_errf("Invalid MAC address format"); + return -1; + } + ether_addr->addr_bytes[i] = strtol(addr_parts[i], NULL, 16); + } + + return 0; +} + +char* get_cfg_key(char *str) +{ + char *pkey = strchr(str, '='); + + if (pkey == NULL) { + return NULL; + } + *pkey++ = '\0'; + + /* remove leading spaces */ + while (isspace(*pkey)) { + pkey++; + } + if (*pkey == '\0') { /* an empty key */ + return NULL; + } + + return pkey; +} + +void strip_spaces(char *strings[], const uint32_t count) +{ + for (uint32_t i = 0; i < count; ++i) { + while (isspace(strings[i][0])) { + ++strings[i]; + } + size_t len = strlen(strings[i]); + + while (len && isspace(strings[i][len - 1])) { + strings[i][len - 1] = '\0'; + --len; + } + } +} + +int is_virtualized(void) +{ + char buf[1024]= "/proc/cpuinfo"; + int virtualized = 0; + FILE* fd = fopen(buf, "r"); + if (fd == NULL) { + set_errf("Could not open %s", buf); + return -1; + } + while (fgets(buf, sizeof(buf), fd) != NULL) { + if ((strstr(buf, "flags") != NULL) && (strstr(buf, "hypervisor") != NULL)) + virtualized = 1; + } + fclose(fd); + return virtualized; +} + +static int get_phys_core(uint32_t *dst, int lcore_id) +{ + uint32_t ret; + char buf[1024]; + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%u/topology/thread_siblings_list", lcore_id); + FILE* ht_fd = fopen(buf, "r"); + + if (ht_fd == NULL) { + set_errf("Could not open cpu topology %s", buf); + return -1; + } + + if (fgets(buf, sizeof(buf), ht_fd) == NULL) { + set_errf("Could not read cpu topology"); + return -1; + } + fclose(ht_fd); + + uint32_t list[2] = {-1,-1}; + parse_list_set(list, buf, 2); + + *dst = list[0]; + + return 0; +} + +static int get_socket(uint32_t core_id, uint32_t *socket) +{ + int ret = -1; + char buf[1024]; + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%u/topology/physical_package_id", core_id); + FILE* fd = fopen(buf, "r"); + + if (fd == NULL) { + set_errf("%s", buf); + return -1; + } + + if (fgets(buf, sizeof(buf), fd) != NULL) { + ret = atoi(buf); + } + fclose(fd); + + if (socket) + *socket = (ret == -1 ? 0 : ret); + + return 0; +} + +int lcore_to_socket_core_ht(uint32_t lcore_id, char *dst, size_t len) +{ + if (cpu_topo.n_sockets == 0) { + if (read_cpu_topology() == -1) { + return -1; + } + } + + for (uint32_t s = 0; s < cpu_topo.n_sockets; s++) { + for (uint32_t i = 0; i < cpu_topo.n_cores[s]; ++i) { + if ((uint32_t)cpu_topo.socket[s][i][0] == lcore_id) { + snprintf(dst, len, "%us%u", i, s); + return 0; + } else if ((uint32_t)cpu_topo.socket[s][i][1] == lcore_id) { + snprintf(dst, len, "%us%uh", i, s); + return 0; + } + } + } + + return -1; +} + +static int get_lcore_id(uint32_t socket_id, uint32_t core_id, int ht) +{ + if (cpu_topo.n_sockets == 0) { + if (read_cpu_topology() == -1) { + return -1; + } + } + + if (socket_id == UINT32_MAX) + socket_id = 0; + + if (socket_id >= MAX_SOCKETS) { + set_errf("Socket id %d too high (max allowed is %d)", MAX_SOCKETS); + return -1; + } + if (core_id >= RTE_MAX_LCORE) { + set_errf("Core id %d too high (max allowed is %d)", RTE_MAX_LCORE); + return -1; + } + if (socket_id >= cpu_topo.n_sockets) { + set_errf("Current CPU topology reported that there are %u CPU sockets, CPU topology = %u socket(s), %u physical cores per socket, %u thread(s) per physical core", + cpu_topo.n_sockets, cpu_topo.n_sockets, cpu_topo.n_cores[0], cpu_topo.socket[0][0][1] == -1? 1: 2); + return -1; + } + if (core_id >= cpu_topo.n_cores[socket_id]) { + set_errf("Core %u on socket %u does not exist, CPU topology = %u socket(s), %u physical cores per socket, %u thread(s) per physical core", + core_id, socket_id, cpu_topo.n_sockets, cpu_topo.n_cores[0], cpu_topo.socket[socket_id][0][1] == -1? 1: 2); + return -1; + } + if (cpu_topo.socket[socket_id][core_id][!!ht] == -1) { + set_errf("Core %u %son socket %u has no hyper-thread, CPU topology = %u socket(s), %u physical cores per socket, %u thread(s) per physical core", + core_id, ht ? "(hyper-thread) " : "", socket_id, cpu_topo.n_sockets, cpu_topo.n_cores[0], cpu_topo.socket[socket_id][core_id][1] == -1? 1: 2); + + return -1; + } + return cpu_topo.socket[socket_id][core_id][!!ht]; +} + +/* Returns 0 on success, negative on error. Parses the syntax XsYh + where sYh is optional. If sY is specified, Y is stored in the + socket argument. If, in addition, h is specified, *ht is set to + 1. In case the input is only a number, socket and ht are set to + -1.*/ +static int parse_core(int *socket, int *core, int *ht, const char* str) +{ + *socket = -1; + *core = -1; + *ht = -1; + + char* end; + + *core = strtol(str, &end, 10); + + if (*end == 's') { + *socket = 0; + *ht = 0; + + if (cpu_topo.n_sockets == 0) { + if (read_cpu_topology() == -1) { + return -1; + } + } + + ++end; + *socket = strtol(end, &end, 10); + if (*socket >= MAX_SOCKETS) { + set_errf("Socket id %d too high (max allowed is %d)", *socket, MAX_SOCKETS - 1); + return -1; + } + + if (*end == 'h') { + ++end; + *ht = 1; + } + + return 0; + } + + if (*end == 'h') { + set_errf("Can't find hyper-thread since socket has not been specified"); + return -1; + } + + return 0; +} + +static int parse_task(const char *str, uint32_t *socket, uint32_t *core, uint32_t *task, uint32_t *ht, enum ctrl_type *type) +{ + const char *str_beg = str; + char *end; + + *core = strtol(str, &end, 10); + if (str == end) { + set_errf("Expected number to in core-task definition:\n" + "\t(i.e. 5s1t0 for task 0 on core 5 on socket 1)\n" + "\tHave: '%s'.", end); + return -1; + } + + *task = 0; + *socket = -1; + *ht = -1; + *type = 0; + + str = end; + + if (*str == 's') { + str++; + *socket = 0; + *ht = 0; + + *socket = strtol(str, &end, 10); + str = end; + + if (*str == 'h') { + str++; + *ht = 1; + } + if (*str == 't') { + str++; + *task = strtol(str, &end, 10); + str = end; + if (*str == 'p') { + *type = CTRL_TYPE_PKT; + str += 1; + } + else if (*str == 'm') { + *type = CTRL_TYPE_MSG; + str += 1; + } + } + } else { + if (*str == 'h') { + set_errf("Can't find hyper-thread since socket has not been specified"); + return -1; + } + if (*str == 't') { + str++; + *task = strtol(str, &end, 10); + str = end; + if (*str == 'p') { + *type = CTRL_TYPE_PKT; + str += 1; + } + else if (*str == 'm') { + *type = CTRL_TYPE_MSG; + str += 1; + } + } + } + return str - str_beg; +} + +static int core_task_set_add(struct core_task_set *val, uint32_t core, uint32_t task, enum ctrl_type type) +{ + if (val->n_elems == sizeof(val->core_task)/sizeof(val->core_task[0])) + return -1; + + val->core_task[val->n_elems].core = core; + val->core_task[val->n_elems].task = task; + val->core_task[val->n_elems].type = type; + val->n_elems++; + + return 0; +} + +int parse_task_set(struct core_task_set *cts, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + cts->n_elems = 0; + + char *str3 = str; + int ret; + + uint32_t socket_beg, core_beg, task_beg, ht_beg, + socket_end, core_end, task_end, ht_end; + enum ctrl_type type_beg, type_end; + uint32_t task_group_start = -1; + + while (*str3 && *str3 != ' ') { + if (*str3 == '(') { + task_group_start = cts->n_elems; + str3 += 1; + continue; + } + if (*str3 == ')' && *(str3 + 1) == 't') { + str3 += 2; + char *end; + uint32_t t = strtol(str3, &end, 10); + enum ctrl_type type = 0; + str3 = end; + + if (*str3 == 'p') { + type = CTRL_TYPE_PKT; + str3 += 1; + } + else if (*str3 == 'm') { + type = CTRL_TYPE_MSG; + str3 += 1; + } + + for (uint32_t i = task_group_start; i < cts->n_elems; ++i) { + cts->core_task[i].task = t; + cts->core_task[i].type = type; + } + continue; + } + ret = parse_task(str3, &socket_beg, &core_beg, &task_beg, &ht_beg, &type_beg); + if (ret < 0) + return -1; + str3 += ret; + socket_end = socket_beg; + core_end = core_beg; + task_end = task_beg; + ht_end = ht_beg; + type_end = type_beg; + + if (*str3 == '-') { + str3 += 1; + ret = parse_task(str3, &socket_end, &core_end, &task_end, &ht_end, &type_end); + if (ret < 0) + return -1; + str3 += ret; + } + + if (*str3 == ',') + str3 += 1; + + if (socket_end != socket_beg) { + set_errf("Same socket must be used in range syntax."); + return -1; + } else if (ht_beg != ht_end) { + set_errf("If 'h' syntax is in range, it must be specified everywhere.\n"); + return -1; + } else if (task_end != task_beg && core_end != core_beg) { + set_errf("Same task must be used in range syntax when cores are different.\n"); + return -1; + } else if (task_end < task_beg) { + set_errf("Task for end of range must be higher than task for beginning of range.\n"); + return -1; + } else if (type_end != type_beg) { + set_errf("Task type for end of range must be the same as task type for beginning.\n"); + return -1; + } else if (core_end < core_beg) { + set_errf("Core for end of range must be higher than core for beginning of range.\n"); + return -1; + } + + for (uint32_t j = core_beg; j <= core_end; ++j) { + if (socket_beg != UINT32_MAX && ht_beg != UINT32_MAX) + ret = get_lcore_id(socket_beg, j, ht_beg); + else + ret = j; + if (ret < 0) + return -1; + for (uint32_t k = task_beg; k <= task_end; ++k) { + core_task_set_add(cts, ret, k, type_beg); + } + } + } + return 0; +} + +int parse_list_set(uint32_t *list, const char *str2, uint32_t max_list) +{ + char str[MAX_STR_LEN_PROC]; + char *parts[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + int n_parts = rte_strsplit(str, strlen(str), parts, MAX_STR_LEN_PROC, ','); + size_t list_count = 0; + + for (int i = 0; i < n_parts; ++i) { + char *cur_part = parts[i]; + char *sub_parts[3]; + int n_sub_parts = rte_strsplit(cur_part, strlen(cur_part), sub_parts, 3, '-'); + int socket1, socket2; + int ht1, ht2; + int core1, core2; + int ret = 0; + + if (n_sub_parts == 1) { + if (parse_core(&socket1, &core1, &ht1, sub_parts[0])) + return -1; + + socket2 = socket1; + core2 = core1; + ht2 = ht1; + } else if (n_sub_parts == 2) { + if (parse_core(&socket1, &core1, &ht1, sub_parts[0])) + return -1; + if (parse_core(&socket2, &core2, &ht2, sub_parts[1])) + return -1; + } else if (n_sub_parts >= 3) { + set_errf("Multiple '-' characters in range syntax found"); + return -1; + } else { + set_errf("Invalid list syntax"); + return -1; + } + + if (socket1 != socket2) { + set_errf("Same socket must be used in range syntax"); + return -1; + } + else if (ht1 != ht2) { + set_errf("If 'h' syntax is in range, it must be specified everywhere."); + return -1; + } + + for (int cur_core = core1; cur_core <= core2; ++cur_core) { + int effective_core; + + if (socket1 != -1) + effective_core = get_lcore_id(socket1, cur_core, ht1); + else + effective_core = cur_core; + + if (list_count >= max_list) { + set_errf("Too many elements in list\n"); + return -1; + } + list[list_count++] = effective_core; + } + } + + return list_count; +} + +int parse_kmg(uint32_t* val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + char c = str[strlen(str) - 1]; + *val = atoi(str); + + switch (c) { + case 'G': + if (*val >> 22) + return -2; + *val <<= 10; + case 'M': + if (*val >> 22) + return -2; + *val <<= 10; + case 'K': + if (*val >> 22) + return -2; + *val <<= 10; + break; + default: + /* only support optional KMG suffix */ + if (c < '0' || c > '9') { + set_errf("Unknown syntax for KMG suffix '%c' (expected K, M or G)", c); + return -1; + } + } + + return 0; +} + +int parse_bool(uint32_t* val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + if (!strcmp(str, "yes")) { + *val = 1; + return 0; + } + else if (!strcmp(str, "no")) { + *val = 0; + return 0; + } + set_errf("Unknown syntax for bool '%s' (expected yes or no)", str); + return -1; +} + +int parse_flag(uint32_t* val, uint32_t flag, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + uint32_t tmp; + if (parse_bool(&tmp, str)) + return -1; + + if (tmp) + *val |= flag; + else + *val &= ~flag; + + return 0; +} + +int parse_int(uint32_t* val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + int64_t tmp = strtol(str, 0, 0); + if (tmp > UINT32_MAX) { + set_errf("Integer is bigger than %u", UINT32_MAX); + return -1; + } + if (tmp < 0) { + set_errf("Integer is negative"); + return -2; + } + *val = tmp; + + return 0; +} + +int parse_float(float* val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + float tmp = strtof(str, 0); + if ((tmp >= HUGE_VALF) || (tmp <= -HUGE_VALF)) { + set_errf("Unable to parse float\n"); + return -1; + } + *val = tmp; + + return 0; +} + +int parse_u64(uint64_t* val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + errno = 0; + uint64_t tmp = strtoul(str, NULL, 0); + if (errno != 0) { + set_errf("Invalid u64 '%s' (%s)", str, strerror(errno)); + return -2; + } + *val = tmp; + + return 0; +} + +int parse_str(char* dst, const char *str2, size_t max_len) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + if (strlen(str) > max_len - 1) { + set_errf("String too long (%u > %u)", strlen(str), max_len - 1); + return -2; + } + + strncpy(dst, str, max_len); + return 0; +} + +int parse_path(char *dst, const char *str, size_t max_len) +{ + if (parse_str(dst, str, max_len)) + return -1; + if (access(dst, F_OK)) { + set_errf("Invalid file '%s' (%s)", dst, strerror(errno)); + return -1; + } + return 0; +} + +int parse_port_name(uint32_t *val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + for (uint8_t i = 0; i < nb_port_names; ++i) { + if (!strcmp(str, port_names[i].name)) { + *val = port_names[i].id; + return 0; + } + } + set_errf("Port with name %s not defined", str); + return 1; +} + +int parse_port_name_list(uint32_t *val, uint32_t* tot, uint8_t max_vals, const char *str2) +{ + char *elements[PROX_MAX_PORTS + 1]; + char str[MAX_STR_LEN_PROC]; + uint32_t cur; + int ret; + + if (parse_str(str, str2, sizeof(str))) + return -1; + + ret = rte_strsplit(str, strlen(str), elements, PROX_MAX_PORTS + 1, ','); + + if (ret == PROX_MAX_PORTS + 1 || ret > max_vals) { + set_errf("Too many ports in port list"); + return -1; + } + + strip_spaces(elements, ret); + for (uint8_t i = 0; i < ret; ++i) { + if (parse_port_name(&cur, elements[i])) { + return -1; + } + val[i] = cur; + } + if (tot) { + *tot = ret; + } + return 0; +} + +int parse_remap(uint8_t *mapping, const char *str) +{ + char *elements[PROX_MAX_PORTS + 1]; + char *elements2[PROX_MAX_PORTS + 1]; + char str_cpy[MAX_STR_LEN_PROC]; + uint32_t val; + int ret, ret2; + + if (strlen(str) > MAX_STR_LEN_PROC) { + set_errf("String too long (max supported: %d)", MAX_STR_LEN_PROC); + return -2; + } + strncpy(str_cpy, str, MAX_STR_LEN_PROC); + + ret = rte_strsplit(str_cpy, strlen(str_cpy), elements, PROX_MAX_PORTS + 1, ','); + if (ret <= 0) { + set_errf("Invalid remap syntax"); + return -1; + } + else if (ret > PROX_MAX_PORTS) { + set_errf("Too many remaps"); + return -2; + } + + strip_spaces(elements, ret); + for (uint8_t i = 0; i < ret; ++i) { + ret2 = rte_strsplit(elements[i], strlen(elements[i]), elements2, PROX_MAX_PORTS + 1, '|'); + strip_spaces(elements2, ret2); + if (ret2 > PROX_MAX_PORTS) { + set_errf("Too many remaps"); + return -2; + } + for (uint8_t j = 0; j < ret2; ++j) { + if (parse_port_name(&val, elements2[j])) { + return -1; + } + + /* This port will be mapped to the i'th + element specified before remap=. */ + mapping[val] = i; + } + } + + return ret; +} + +int add_port_name(uint32_t val, const char *str2) +{ + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + struct port_name* pn; + + if (nb_port_names == MAX_NB_PORT_NAMES) { + set_errf("Too many ports defined (can define %d)", MAX_NB_PORT_NAMES); + return -1; + } + + for (uint8_t i = 0; i < nb_port_names; ++i) { + /* each port has to have a unique name*/ + if (!strcmp(str, port_names[i].name)) { + set_errf("Port with name %s is already defined", str); + return -2; + } + } + + pn = &port_names[nb_port_names]; + strncpy(pn->name, str, sizeof(pn->name)); + pn->id = val; + + ++nb_port_names; + return 0; +} + +int set_self_var(const char *str) +{ + for (uint8_t i = 0; i < nb_vars; ++i) { + if (!strcmp("$self", vars[i].name)) { + sprintf(vars[i].val, "%s", str); + return 0; + } + } + + struct var *v = &vars[nb_vars]; + + strncpy(v->name, "$self", strlen("$self")); + sprintf(v->val, "%s", str); + nb_vars++; + + return 0; +} + +int add_var(const char* name, const char *str2, uint8_t cli) +{ + struct var* v; + + char str[MAX_STR_LEN_PROC]; + + if (parse_vars(str, sizeof(str), str2)) + return -1; + + if (strlen(name) == 0 || strlen(name) == 1) { + set_errf("Can't define variables with empty name"); + return -1; + } + + if (name[0] != '$') { + set_errf("Each variable should start with the $ character"); + return -1; + } + + if (nb_vars == MAX_NB_VARS) { + set_errf("Too many variables defined (can define %d)", MAX_NB_VARS); + return -2; + } + + for (uint8_t i = 0; i < nb_vars; ++i) { + if (!strcmp(name, vars[i].name)) { + + /* Variables defined through program arguments + take precedence. */ + if (!cli && vars[i].cli) { + return 0; + } + + set_errf("Variable with name %s is already defined", name); + return -3; + } + } + + v = &vars[nb_vars]; + PROX_PANIC(strlen(name) > sizeof(v->name), "\tUnable to parse var %s: too long\n", name); + PROX_PANIC(strlen(str) > sizeof(v->val), "\tUnable to parse var %s=%s: too long\n", name,str); + strncpy(v->name, name, sizeof(v->name)); + strncpy(v->val, str, sizeof(v->val)); + v->cli = cli; + + ++nb_vars; + return 0; +} + +static int read_cores_present(uint32_t *cores, int max_cores, int *res) +{ + FILE* fd = fopen("/sys/devices/system/cpu/present", "r"); + char buf[1024]; + + if (fd == NULL) { + set_errf("Could not opening file /sys/devices/system/cpu/present"); + return -1; + } + + if (fgets(buf, sizeof(buf), fd) == NULL) { + set_errf("Could not read cores range"); + return -1; + } + + fclose(fd); + + int ret = parse_list_set(cores, buf, max_cores); + + if (ret < 0) + return -1; + + *res = ret; + return 0; +} + +static int set_dummy_topology(void) +{ + int core_count = 0; + + for (int s = 0; s < MAX_SOCKETS; s++) { + for (int i = 0; i < 32; ++i) { + cpu_topo.socket[s][i][0] = core_count++; + cpu_topo.socket[s][i][1] = core_count++; + cpu_topo.n_cores[s]++; + } + } + cpu_topo.n_sockets = MAX_SOCKETS; + return 0; +} + +static int read_cpu_topology(void) +{ + if (cpu_topo.n_sockets != 0) + return 0; + if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) + return set_dummy_topology(); + + uint32_t cores[RTE_MAX_LCORE]; + int n_cores = 0; + + if (read_cores_present(cores, sizeof(cores)/sizeof(cores[0]), &n_cores) != 0) + return -1; + + for (int s = 0; s < MAX_SOCKETS; s++) { + for (int i = 0; i < RTE_MAX_LCORE; ++i) { + cpu_topo.socket[s][i][0] = -1; + cpu_topo.socket[s][i][1] = -1; + } + } + + for (int i = 0; i < n_cores; ++i) { + uint32_t socket_id, lcore_id, phys; + + lcore_id = cores[i]; + if (get_socket(lcore_id, &socket_id) != 0) + return -1; + if (socket_id >= MAX_SOCKETS) { + set_errf("Can't read CPU topology due too high socket ID (max allowed is %d)", + MAX_SOCKETS); + return -1; + } + if (socket_id >= cpu_topo.n_sockets) { + cpu_topo.n_sockets = socket_id + 1; + } + if (get_phys_core(&phys, lcore_id) != 0) + return -1; + if (phys >= RTE_MAX_LCORE) { + set_errf("Core ID %u too high", phys); + return -1; + } + + if (cpu_topo.socket[socket_id][phys][0] == -1) { + cpu_topo.socket[socket_id][phys][0] = lcore_id; + cpu_topo.n_cores[socket_id]++; + } + else if (cpu_topo.socket[socket_id][phys][1] == -1) { + cpu_topo.socket[socket_id][phys][1] = lcore_id; + } + else { + set_errf("Too many core siblings"); + return -1; + } + } + + /* There can be holes in the cpu_topo description at this + point. An example for this is a CPU topology where the + lowest core ID of 2 hyper-threads is always an even + number. Before finished up this phase, compact all the + cores to make the numbers consecutive. */ + + for (uint32_t i = 0; i < cpu_topo.n_sockets; ++i) { + int spread = 0, compact = 0; + while (cpu_topo.socket[i][spread][0] == -1) + spread++; + + for (uint32_t c = 0; c < cpu_topo.n_cores[i]; ++c) { + cpu_topo.socket[i][compact][0] = cpu_topo.socket[i][spread][0]; + cpu_topo.socket[i][compact][1] = cpu_topo.socket[i][spread][1]; + compact++; + spread++; + /* Skip gaps */ + while (cpu_topo.socket[i][spread][0] == -1) + spread++; + } + } + + return 0; +} + +static int bit_len_valid(uint32_t len, const char *str) +{ + if (len > 32) { + set_errf("Maximum random length is 32, but length of '%s' is %zu\n", str, len); + return 0; + } + if (len % 8) { + plog_err("Random should be multiple of 8 long\n"); + return 0; + } + if (len == 0) { + plog_err("Random should be at least 1 byte long\n"); + return 0; + } + return -1; +} + +int parse_random_str(uint32_t *mask, uint32_t *fixed, uint32_t *len, const char *str) +{ + const size_t len_bits = strlen(str); + + if (!bit_len_valid(len_bits, str)) + return -1; + + *mask = 0; + *fixed = 0; + *len = len_bits / 8; + + for (uint32_t j = 0; j < len_bits; ++j) { + /* Store in the lower bits the value of the rand string (note + that these are the higher bits in LE). */ + switch (str[j]) { + case 'X': + *mask |= 1 << (len_bits - 1 - j); + break; + case '1': + *fixed |= 1 << (len_bits - 1 - j); + break; + case '0': + break; + default: + set_errf("Unexpected %c\n", str[j]); + return -1; + } + } + return 0; +} diff --git a/VNFs/DPPD-PROX/parse_utils.h b/VNFs/DPPD-PROX/parse_utils.h new file mode 100644 index 00000000..14aee9eb --- /dev/null +++ b/VNFs/DPPD-PROX/parse_utils.h @@ -0,0 +1,121 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PARSE_UTILS_H_ +#define _PARSE_UTILS_H_ + +#include <inttypes.h> +#include "ip_subnet.h" + +#define MAX_STR_LEN_PROC (3 * 1518 + 20) + +struct ipv6_addr; +struct ether_addr; + +enum ctrl_type {CTRL_TYPE_DP, CTRL_TYPE_MSG, CTRL_TYPE_PKT}; + +struct core_task { + uint32_t core; + uint32_t task; + enum ctrl_type type; +}; + +struct core_task_set { + struct core_task core_task[64]; + uint32_t n_elems; +}; + +int parse_vars(char *val, size_t len, const char *name); + +int parse_int_mask(uint32_t* val, uint32_t* mask, const char *saddr); + +int parse_range(uint32_t* lo, uint32_t* hi, const char *saddr); + +/* parses CIDR notation. Note that bits within the address that are + outside the subnet (as specified by the prefix) are set to 0. */ +int parse_ip4_cidr(struct ip4_subnet *val, const char *saddr); +int parse_ip6_cidr(struct ip6_subnet *val, const char *saddr); + +int parse_ip(uint32_t *paddr, const char *saddr); + +int parse_ip6(struct ipv6_addr *addr, const char *saddr); + +int parse_mac(struct ether_addr *paddr, const char *saddr); + +/* return error on overflow or invalid suffix*/ +int parse_kmg(uint32_t* val, const char *str); + +int parse_bool(uint32_t* val, const char *str); + +int parse_flag(uint32_t* val, uint32_t flag, const char *str); + +int parse_list_set(uint32_t *list, const char *str, uint32_t max_limit); + +int parse_task_set(struct core_task_set *val, const char *str); + +int parse_int(uint32_t* val, const char *str); +int parse_float(float* val, const char *str); + +int parse_u64(uint64_t* val, const char *str); + +int parse_str(char* dst, const char *str, size_t max_len); + +int parse_path(char *dst, const char *str, size_t max_len); + +int parse_port_name(uint32_t *val, const char *str); + +/* The syntax for random fields is X0010101XXX... where X is a + randomized bit and 0, 1 are fixed bit. The resulting mask and fixed + arguments are in BE order. */ +int parse_random_str(uint32_t *mask, uint32_t *fixed, uint32_t *len, const char *str); + +int parse_port_name_list(uint32_t *val, uint32_t *tot, uint8_t max_vals, const char *str); + +/* Parses a comma separated list containing a remapping of ports + specified by their name. Hence, all port names referenced from the + list have to be added using add_port_name() before this function + can be used. The first elements in the list are mapped to 0, the + second to 1, etc. Multiple elements can be mapped to the same + index. If multiple elements are used, they are separated by + pipes. An example would be p0|p1,p2|p3. In this example, p0 and p1 + both map to 0 and p2 and p3 map both map to 1. The mapping should + contain at least enough entries as port ids. */ +int parse_remap(uint8_t *mapping, const char *str); + +/* Convert an lcore_id to socket notation */ +int lcore_to_socket_core_ht(uint32_t lcore_id, char *dst, size_t len); + +int add_port_name(uint32_t val, const char *str); + +/* The $self variable is something that can change its value (i.e. its + value represents the core that is currently being parsed). */ +int set_self_var(const char *str); + +int add_var(const char* name, const char *val, uint8_t cli); + +/* Parses str and returns pointer to the key value */ +char *get_cfg_key(char *str); + +/* Changes strings in place. */ +void strip_spaces(char *strings[], const uint32_t count); + +/* Contains error string if any of the above returned an error. */ +const char* get_parse_err(void); + +/* Returns true if running from a virtual machine. */ +int is_virtualized(void); + +#endif /* _PARSE_UTILS_H_ */ diff --git a/VNFs/DPPD-PROX/pkt_parser.h b/VNFs/DPPD-PROX/pkt_parser.h new file mode 100644 index 00000000..285d42f9 --- /dev/null +++ b/VNFs/DPPD-PROX/pkt_parser.h @@ -0,0 +1,178 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PKT_PARSER_H_ +#define _PKT_PARSER_H_ + +#include <rte_mbuf.h> +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_tcp.h> +#include <rte_byteorder.h> + +#include "log.h" +#include "etypes.h" + +struct pkt_tuple { + uint32_t src_addr; + uint32_t dst_addr; + uint8_t proto_id; + uint16_t src_port; + uint16_t dst_port; + uint16_t l2_types[4]; +} __attribute__((packed)); + +struct l4_meta { + uint8_t *l4_hdr; + uint8_t *payload; + uint16_t len; +}; + +static void pkt_tuple_debug2(const struct pkt_tuple *pt) +{ + plogx_info("src_ip : %#010x\n", pt->src_addr); + plogx_info("dst_ip : %#010x\n", pt->dst_addr); + plogx_info("dst_port : %#06x\n", pt->dst_port); + plogx_info("src_port : %#06x\n", pt->src_port); + plogx_info("proto_id : %#04x\n", pt->proto_id); + plogx_info("l2 types: \n"); + for (int i = 0; i < 4; ++i) + plogx_info(" - %#04x\n", pt->l2_types[i]); +} + +static void pkt_tuple_debug(const struct pkt_tuple *pt) +{ + plogx_dbg("src_ip : %#010x\n", pt->src_addr); + plogx_dbg("dst_ip : %#010x\n", pt->dst_addr); + plogx_dbg("dst_port : %#06x\n", pt->dst_port); + plogx_dbg("src_port : %#06x\n", pt->src_port); + plogx_dbg("proto_id : %#04x\n", pt->proto_id); + plogx_dbg("l2 types: \n"); + for (int i = 0; i < 4; ++i) + plogx_dbg(" - %#04x\n", pt->l2_types[i]); +} + +/* Return 0 on success, i.e. packets parsed without any error. */ +static int parse_pkt(struct rte_mbuf *mbuf, struct pkt_tuple *pt, struct l4_meta *l4_meta) +{ + struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + size_t l2_types_count = 0; + struct ipv4_hdr* pip = 0; + + /* L2 */ + pt->l2_types[l2_types_count++] = peth->ether_type; + + switch (peth->ether_type) { + case ETYPE_IPv4: + pip = (struct ipv4_hdr *)(peth + 1); + break; + case ETYPE_VLAN: { + struct vlan_hdr *vlan = (struct vlan_hdr *)(peth + 1); + pt->l2_types[l2_types_count++] = vlan->eth_proto; + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (struct ipv4_hdr *)(peth + 1); + } + else if (vlan->eth_proto == ETYPE_VLAN) { + struct vlan_hdr *vlan = (struct vlan_hdr *)(peth + 1); + pt->l2_types[l2_types_count++] = vlan->eth_proto; + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (struct ipv4_hdr *)(peth + 1); + } + else if (vlan->eth_proto == ETYPE_IPv6) { + return 1; + } + else { + /* TODO: handle BAD PACKET */ + return 1; + } + } + } + break; + case ETYPE_8021ad: { + struct vlan_hdr *vlan = (struct vlan_hdr *)(peth + 1); + pt->l2_types[l2_types_count++] = vlan->eth_proto; + if (vlan->eth_proto == ETYPE_VLAN) { + struct vlan_hdr *vlan = (struct vlan_hdr *)(peth + 1); + pt->l2_types[l2_types_count++] = vlan->eth_proto; + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (struct ipv4_hdr *)(peth + 1); + } + else { + return 1; + } + } + else { + return 1; + } + } + break; + case ETYPE_MPLSU: + return -1; + break; + default: + plogx_err("Parsing error: unknown packet ether type = %#06x\n", peth->ether_type); + return -1; + break; + } + + /* L3 */ + if ((pip->version_ihl >> 4) == 4) { + + if ((pip->version_ihl & 0x0f) != 0x05) { + /* TODO: optional fields */ + return 1; + } + + pt->proto_id = pip->next_proto_id; + pt->src_addr = pip->src_addr; + pt->dst_addr = pip->dst_addr; + } + else { + /* TODO: IPv6 and bad packets */ + return 1; + } + + /* L4 parser */ + if (pt->proto_id == IPPROTO_UDP) { + struct udp_hdr *udp = (struct udp_hdr*)(pip + 1); + l4_meta->l4_hdr = (uint8_t*)udp; + pt->src_port = udp->src_port; + pt->dst_port = udp->dst_port; + l4_meta->payload = ((uint8_t*)udp) + sizeof(struct udp_hdr); + l4_meta->len = rte_be_to_cpu_16(udp->dgram_len) - sizeof(struct udp_hdr); + } + else if (pt->proto_id == IPPROTO_TCP) { + struct tcp_hdr *tcp = (struct tcp_hdr*)(pip + 1); + l4_meta->l4_hdr = (uint8_t*)tcp; + pt->src_port = tcp->src_port; + pt->dst_port = tcp->dst_port; + + l4_meta->payload = ((uint8_t*)tcp) + ((tcp->data_off >> 4)*4); + l4_meta->len = rte_be_to_cpu_16(pip->total_length) - sizeof(struct ipv4_hdr) - ((tcp->data_off >> 4)*4); + } + else { + plog_err("unsupported protocol %d\n", pt->proto_id); + return 1; + } + + for (; l2_types_count < sizeof(pt->l2_types)/sizeof(pt->l2_types[0]); ++l2_types_count) + pt->l2_types[l2_types_count] = 0; + + return 0; +} + +#endif /* _PKT_PARSER_H_ */ diff --git a/VNFs/DPPD-PROX/pkt_prototypes.h b/VNFs/DPPD-PROX/pkt_prototypes.h new file mode 100644 index 00000000..5d55bacb --- /dev/null +++ b/VNFs/DPPD-PROX/pkt_prototypes.h @@ -0,0 +1,54 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PKT_PROTOTYPES_H_ +#define _PKT_PROTOTYPES_H_ + +#include <rte_ip.h> + +#include "gre.h" +#include "qinq.h" +#include "etypes.h" + +static const struct gre_hdr gre_hdr_proto = { + .type = ETYPE_IPv4, + .version = 0, + .flags = 0, + .recur = 0, + .bits = GRE_KEY_PRESENT +}; + +static const struct ipv4_hdr tunnel_ip_proto = { + .version_ihl = 0x45, + .type_of_service = 0, + .packet_id = 0, + .fragment_offset = 0x40, + /* no fragmentation */ + .time_to_live = 0x40, + /* gre protocol type */ + .next_proto_id = IPPROTO_GRE, + .hdr_checksum = 0 +}; + +static const struct qinq_hdr qinq_proto = { + .svlan.vlan_tci = 0, + .cvlan.vlan_tci = 0, + .svlan.eth_proto = ETYPE_8021ad, + .cvlan.eth_proto = ETYPE_VLAN, + .ether_type = ETYPE_IPv4 +}; + +#endif /* _PKT_PROTOTYPES_H_ */ diff --git a/VNFs/DPPD-PROX/prefetch.h b/VNFs/DPPD-PROX/prefetch.h new file mode 100644 index 00000000..a42afe4a --- /dev/null +++ b/VNFs/DPPD-PROX/prefetch.h @@ -0,0 +1,63 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PREFETCH_H_ +#define _PREFETCH_H_ + +#include <rte_mbuf.h> + +static inline void prefetch_nta(volatile void *p) +{ + asm volatile ("prefetchnta %[p]" : [p] "+m" (*(volatile char *)p)); +} + +#ifdef PROX_PREFETCH_OFFSET +#define PREFETCH0(p) rte_prefetch0(p) +#define PREFETCH_OFFSET PROX_PREFETCH_OFFSET +#else +#define PREFETCH0(p) do {} while (0) +#define PREFETCH_OFFSET 0 +#endif + +static inline void prefetch_pkts(__attribute__((unused)) struct rte_mbuf **mbufs, __attribute__((unused)) uint16_t n_pkts) +{ +#ifdef PROX_PREFETCH_OFFSET + for (uint16_t j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (uint16_t j = PROX_PREFETCH_OFFSET; j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j - PROX_PREFETCH_OFFSET], void*)); + } + for (uint16_t j = n_pkts - PROX_PREFETCH_OFFSET; j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void*)); + } +#endif +} + +static inline void prefetch_first(__attribute__((unused)) struct rte_mbuf **mbufs, __attribute__((unused)) uint16_t n_pkts) +{ +#ifdef PROX_PREFETCH_OFFSET + for (uint16_t j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (uint16_t j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif +} + +#endif /* _PREFETCH_H_ */ diff --git a/VNFs/DPPD-PROX/prox_args.c b/VNFs/DPPD-PROX/prox_args.c new file mode 100644 index 00000000..df9a2ca4 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_args.c @@ -0,0 +1,1975 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <unistd.h> +#include <string.h> + +#include <rte_sched.h> +#include <rte_string_fns.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "version.h" +#include "defines.h" +#include "prox_args.h" +#include "prox_assert.h" +#include "prox_cfg.h" +#include "cfgfile.h" +#include "quit.h" +#include "log.h" +#include "parse_utils.h" +#include "prox_port_cfg.h" +#include "defaults.h" +#include "prox_lua.h" +#include "cqm.h" + +#define MAX_RTE_ARGV 64 +#define MAX_ARG_LEN 64 + +struct cfg_depr { + const char *opt; + const char *info; +}; + +/* Helper macro */ +#define STR_EQ(s1, s2) (!strcmp((s1), (s2))) + +/* configuration files support */ +static int get_rte_cfg(unsigned sindex, char *str, void *data); +static int get_global_cfg(unsigned sindex, char *str, void *data); +static int get_port_cfg(unsigned sindex, char *str, void *data); +static int get_defaults_cfg(unsigned sindex, char *str, void *data); +static int get_cache_set_cfg(unsigned sindex, char *str, void *data); +static int get_var_cfg(unsigned sindex, char *str, void *data); +static int get_lua_cfg(unsigned sindex, char *str, void *data); +static int get_core_cfg(unsigned sindex, char *str, void *data); + +static const char *cfg_file = DEFAULT_CONFIG_FILE; +static struct rte_cfg rte_cfg; +struct prox_cache_set_cfg prox_cache_set_cfg[PROX_MAX_CACHE_SET]; + +static char format_err_str[1024]; +static const char *err_str = "Unknown error"; + +static struct cfg_section eal_default_cfg = { + .name = "eal options", + .parser = get_rte_cfg, + .data = &rte_cfg, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section port_cfg = { + .name = "port #", + .parser = get_port_cfg, + .data = &prox_port_cfg, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section var_cfg = { + .name = "variables", + .parser = get_var_cfg, + .data = 0, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section cache_set_cfg = { + .name = "cache set #", + .parser = get_cache_set_cfg, + .data = &prox_cache_set_cfg, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section defaults_cfg = { + .name = "defaults", + .parser = get_defaults_cfg, + .data = 0, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section settings_cfg = { + .name = "global", + .parser = get_global_cfg, + .data = &prox_cfg, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static struct cfg_section lua_cfg = { + .name = "lua", + .parser = get_lua_cfg, + .raw_lines = 1, + .indexp[0] = 0, + .nbindex = 1, + .error = 0, +}; + +static struct cfg_section core_cfg = { + .name = "core #", + .parser = get_core_cfg, + .data = lcore_cfg_init, + .indexp[0] = 0, + .nbindex = 1, + .error = 0 +}; + +static void set_errf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vsnprintf(format_err_str, sizeof(format_err_str), format, ap); + va_end(ap); + err_str = format_err_str; +} + +/* [eal options] parser */ +static int get_rte_cfg(__attribute__((unused))unsigned sindex, char *str, void *data) +{ + struct rte_cfg *pconfig = (struct rte_cfg *)data; + + if (str == NULL || pconfig == NULL) { + return -1; + } + + char *pkey = get_cfg_key(str); + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + if (STR_EQ(str, "-m")) { + return parse_int(&pconfig->memory, pkey); + } + if (STR_EQ(str, "-n")) { + if (parse_int(&pconfig->force_nchannel, pkey)) { + return -1; + } + if (pconfig->force_nchannel == 0) { + set_errf("Invalid number of memory channels"); + return -1; + } + return 0; + } + if (STR_EQ(str, "-r")) { + if (parse_int(&pconfig->force_nrank, pkey)) { + return -1; + } + if (pconfig->force_nrank == 0 || pconfig->force_nrank > 16) { + set_errf("Invalid number of memory ranks"); + return -1; + } + return 0; + } + /* debug options */ + if (STR_EQ(str, "no-pci")) { + return parse_bool(&pconfig->no_pci, pkey); + } + if (STR_EQ(str, "no-hpet")) { + return parse_bool(&pconfig->no_hpet, pkey); + } + if (STR_EQ(str, "no-shconf")) { + return parse_bool(&pconfig->no_shconf, pkey); + } + if (STR_EQ(str, "no-huge")) { + return parse_bool(&pconfig->no_hugetlbfs, pkey); + } + if (STR_EQ(str, "no-output")) { + return parse_bool(&pconfig->no_output, pkey); + } + + if (STR_EQ(str, "huge-dir")) { + if (pconfig->hugedir) { + free(pconfig->hugedir); + } + pconfig->hugedir = strdup(pkey); + return 0; + } + + if (STR_EQ(str, "eal")) { + char eal[MAX_STR_LEN_PROC]; + if (pconfig->eal) { + free(pconfig->eal); + pconfig->eal = NULL; + } + if (parse_str(eal, pkey, sizeof(eal))) + return -1; + pkey = eal; + strip_spaces(&pkey, 1); + if (*pkey) + pconfig->eal = strdup(pkey); + return 0; + } + + set_errf("Option '%s' is not known", str); + return -1; +} + +struct cfg_depr global_cfg_depr[] = { + {"virtualization", "This is now set automatically if needed"}, + {"qinq_tag", "This option is deprecated"}, + {"wait on quit", "This is now set automatically if needed"}, + {"version", ""} +}; + +const char *get_cfg_dir(void) +{ + static char dir[PATH_MAX]; + size_t end = strlen(cfg_file) - 1; + while (end > 0 && cfg_file[end] != '/') + end--; + + strncpy(dir, cfg_file, end); + return dir; +} + +static int get_lua_cfg(__attribute__((unused)) unsigned sindex, __attribute__((unused)) char *str, __attribute__((unused)) void *data) +{ + int status; + char cwd[1024]; + if (NULL == getcwd(cwd, sizeof(cwd))) { + set_errf("Failed to get current directory while loading Lua file\n"); + return -1; + } + status = chdir(get_cfg_dir()); + if (status) { + set_errf("Failed to change directory to '%s' while loading Lua file\n", get_cfg_dir()); + return -1; + } + + struct lua_State *l = prox_lua(); + + char str_cpy[1024]; + strncpy(str_cpy, str, sizeof(str_cpy)); + uint32_t len = strlen(str_cpy); + str_cpy[len++] = '\n'; + str_cpy[len++] = 0; + + status = luaL_loadstring(l, str_cpy); + if (status) { + set_errf("Lua error: '%s'\n", lua_tostring(l, -1)); + status = chdir(cwd); + return -1; + } + + status = lua_pcall(l, 0, LUA_MULTRET, 0); + if (status) { + set_errf("Lua error: '%s'\n", lua_tostring(l, -1)); + status = chdir(cwd); + return -1; + } + + status = chdir(cwd); + if (status) { + set_errf("Failed to restore current directory to '%s' while loading Lua file\n", cwd); + return -1; + } + + return 0; +} + +/* [global] parser */ +static int get_global_cfg(__attribute__((unused))unsigned sindex, char *str, void *data) +{ + struct prox_cfg *pset = (struct prox_cfg *)data; + + if (str == NULL || pset == NULL) { + return -1; + } + + char *pkey = get_cfg_key(str); + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + for (uint32_t i = 0; i < RTE_DIM(global_cfg_depr); ++i) { + if (STR_EQ(str, global_cfg_depr[i].opt)) { + set_errf("Option '%s' is deprecated%s%s", + global_cfg_depr[i].opt, strlen(global_cfg_depr[i].info)? ": ": "", global_cfg_depr[i].info); + return -1; + } + } + + if (STR_EQ(str, "name")) { + return parse_str(pset->name, pkey, sizeof(pset->name)); + } + + if (STR_EQ(str, "start time")) { + return parse_int(&pset->start_time, pkey); + } + + if (STR_EQ(str, "duration time")) { + return parse_int(&pset->duration_time, pkey); + } + + if (STR_EQ(str, "shuffle")) { + return parse_flag(&pset->flags, DSF_SHUFFLE, pkey); + } + if (STR_EQ(str, "disable cmt")) { + return parse_flag(&pset->flags, DSF_DISABLE_CMT, pkey); + } + if (STR_EQ(str, "mp rings")) { + return parse_flag(&pset->flags, DSF_MP_RINGS, pkey); + } + if (STR_EQ(str, "enable bypass")) { + return parse_flag(&pset->flags, DSF_ENABLE_BYPASS, pkey); + } + + if (STR_EQ(str, "cpe table map")) { + /* The config defined ports through 0, 1, 2 ... which + need to be associated with ports. This is done + through defining it using "cpe table map=" */ + return parse_port_name_list((uint32_t*)pset->cpe_table_ports, NULL, PROX_MAX_PORTS, pkey); + } + + if (STR_EQ(str, "pre cmd")) { + return system(pkey); + } + + if (STR_EQ(str, "unique mempool per socket")) { + return parse_flag(&pset->flags, UNIQUE_MEMPOOL_PER_SOCKET, pkey); + } + + if (STR_EQ(str, "log buffer size")) { + if (parse_kmg(&pset->logbuf_size, pkey)) { + return -1; + } + plog_info("Logging to buffer with size = %d\n", pset->logbuf_size); + return 0; + } + + set_errf("Option '%s' is not known", str); + return -1; +} + +/* [variable] parser */ +static int get_var_cfg(__attribute__((unused)) unsigned sindex, char *str, __attribute__((unused)) void *data) +{ + return add_var(str, get_cfg_key(str), 0); +} + +/* [defaults] parser */ +static int get_defaults_cfg(__attribute__((unused)) unsigned sindex, char *str, __attribute__((unused)) void *data) +{ + uint32_t val; + char *pkey; + + pkey = get_cfg_key(str); + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + if (STR_EQ(str, "mempool size")) { + + if (parse_kmg(&val, pkey)) { + return -1; + } + + for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + struct lcore_cfg *cur_lcore_cfg_init = &lcore_cfg_init[lcore_id]; + cur_lcore_cfg_init->id = lcore_id; + for (uint8_t task_id = 0; task_id < MAX_TASKS_PER_CORE; ++task_id) { + struct task_args *targ = &cur_lcore_cfg_init->targs[task_id]; + targ->nb_mbuf = val; + targ->id = task_id; + } + } + return 0; + } + + if (STR_EQ(str, "qinq tag")) { + for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + struct lcore_cfg *cur_lcore_cfg_init = &lcore_cfg_init[lcore_id]; + cur_lcore_cfg_init->id = lcore_id; + for (uint8_t task_id = 0; task_id < MAX_TASKS_PER_CORE; ++task_id) { + struct task_args *targ = &cur_lcore_cfg_init->targs[task_id]; + parse_int(&targ->qinq_tag, pkey); + } + } + return 0; + } + if (STR_EQ(str, "memcache size")) { + + if (parse_kmg(&val, pkey)) { + return -1; + } + + for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + struct lcore_cfg *cur_lcore_cfg_init = &lcore_cfg_init[lcore_id]; + cur_lcore_cfg_init->id = lcore_id; + for (uint8_t task_id = 0; task_id < MAX_TASKS_PER_CORE; ++task_id) { + struct task_args *targ = &cur_lcore_cfg_init->targs[task_id]; + targ->nb_cache_mbuf = val; + } + } + return 0; + } + + set_errf("Option '%s' is not known", str); + return -1; +} + +/* [cache set] parser */ +static int get_cache_set_cfg(unsigned sindex, char *str, void *data) +{ + struct prox_cache_set_cfg *cfg = (struct prox_cache_set_cfg *)data; + + uint8_t cur_if = sindex & ~CFG_INDEXED; + + if (cur_if >= PROX_MAX_CACHE_SET) { + set_errf("Cache set ID is too high (max allowed %d)", PROX_MAX_CACHE_SET - 1 ); + return -1; + } + + cfg = &prox_cache_set_cfg[cur_if]; + + if (str == NULL || data == NULL) { + return -1; + } + + char *pkey = get_cfg_key(str); + + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + if (STR_EQ(str, "mask")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + cfg->mask = val; + cfg->socket_id = -1; + plog_info("\tCache set %d has mask %x\n", cur_if, cfg->mask); + return 0; + } + return 0; +} + +/* [port] parser */ +static int get_port_cfg(unsigned sindex, char *str, void *data) +{ + struct prox_port_cfg *cfg = (struct prox_port_cfg *)data; + + uint8_t cur_if = sindex & ~CFG_INDEXED; + + if (cur_if >= PROX_MAX_PORTS) { + set_errf("Port ID is too high (max allowed %d)", PROX_MAX_PORTS - 1 ); + return -1; + } + + cfg = &prox_port_cfg[cur_if]; + + if (str == NULL || data == NULL) { + return -1; + } + + char *pkey = get_cfg_key(str); + + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + if (STR_EQ(str, "mac")) { + if (STR_EQ(pkey, "hardware")) { + cfg->type = PROX_PORT_MAC_HW; + } + else if (STR_EQ(pkey, "random")) { + cfg->type = PROX_PORT_MAC_RAND; + } + else { + cfg->type = PROX_PORT_MAC_SET; + if (parse_mac(&cfg->eth_addr, pkey)) { + return -1; + } + } + } + else if (STR_EQ(str, "name")) { + uint32_t val; + strncpy(cfg->name, pkey, MAX_NAME_SIZE); + PROX_ASSERT(cur_if < PROX_MAX_PORTS); + return add_port_name(cur_if, pkey); + } + else if (STR_EQ(str, "rx desc")) { + return parse_int(&cfg->n_rxd, pkey); + } + else if (STR_EQ(str, "tx desc")) { + return parse_int(&cfg->n_txd, pkey); + } + else if (STR_EQ(str, "promiscuous")) { + uint32_t val; + if (parse_bool(&val, pkey)) { + return -1; + } + cfg->promiscuous = val; + } + else if (STR_EQ(str, "lsc")) { + cfg->lsc_set_explicitely = 1; + uint32_t val; + if (parse_bool(&val, pkey)) { + return -1; + } + cfg->lsc_val = val; + } + else if (STR_EQ(str, "strip crc")) { + uint32_t val; + if (parse_bool(&val, pkey)) { + return -1; + } + cfg->port_conf.rxmode.hw_strip_crc = val; + } + else if (STR_EQ(str, "rss")) { + uint32_t val; + if (parse_bool(&val, pkey)) { + return -1; + } + if (val) { + cfg->port_conf.rxmode.mq_mode = ETH_MQ_RX_RSS; + cfg->port_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IPV4; + } + } + else if (STR_EQ(str, "rx_ring")) { + parse_str(cfg->rx_ring, pkey, sizeof(cfg->rx_ring)); + } + else if (STR_EQ(str, "tx_ring")) { + parse_str(cfg->tx_ring, pkey, sizeof(cfg->tx_ring)); + } + + return 0; +} + +static enum police_action str_to_color(const char *str) +{ + if (STR_EQ(str, "green")) + return ACT_GREEN; + if (STR_EQ(str, "yellow")) + return ACT_YELLOW; + if (STR_EQ(str, "red")) + return ACT_RED; + if (STR_EQ(str, "drop")) + return ACT_DROP; + return ACT_INVALID; +} + +struct cfg_depr task_cfg_depr[] = { + {"sig", ""}, +}; + +struct cfg_depr core_cfg_depr[] = { + {"do sig", ""}, + {"lat", ""}, + {"network side", ""}, +}; + +/* [core] parser */ +static int get_core_cfg(unsigned sindex, char *str, void *data) +{ + char *pkey; + struct lcore_cfg *lconf = (struct lcore_cfg *)data; + + if (str == NULL || lconf == NULL || !(sindex & CFG_INDEXED)) { + return -1; + } + + pkey = get_cfg_key(str); + if (pkey == NULL) { + set_errf("Missing key after option"); + return -1; + } + + uint32_t ncore = sindex & ~CFG_INDEXED; + if (ncore >= RTE_MAX_LCORE) { + set_errf("Core index too high (max allowed %d)", RTE_MAX_LCORE - 1); + return -1; + } + + lconf = &lconf[ncore]; + + for (uint32_t i = 0; i < RTE_DIM(core_cfg_depr); ++i) { + if (STR_EQ(str, core_cfg_depr[i].opt)) { + set_errf("Option '%s' is deprecated%s%s", + core_cfg_depr[i].opt, strlen(core_cfg_depr[i].info)? ": ": "", core_cfg_depr[i].info); + return -1; + } + } + + char buff[128]; + lcore_to_socket_core_ht(ncore, buff, sizeof(buff)); + set_self_var(buff); + if (STR_EQ(str, "task")) { + + uint32_t val; + if (parse_int(&val, pkey)) { + return -1; + } + if (val >= MAX_TASKS_PER_CORE) { + set_errf("Too many tasks for core (max allowed %d)", MAX_TASKS_PER_CORE - 1); + return -1; + } + if (val != lconf->n_tasks_all) { + set_errf("Task ID skipped or defined twice"); + return -1; + } + + lconf->active_task = val; + + lconf->targs[lconf->active_task].task = lconf->active_task; + + if (lconf->n_tasks_all < lconf->active_task + 1) { + lconf->n_tasks_all = lconf->active_task + 1; + } + return 0; + } + + struct task_args *targ = &lconf->targs[lconf->active_task]; + if (STR_EQ(str, "tx ports from routing table")) { + uint32_t vals[PROX_MAX_PORTS]; + uint32_t n_if; + if (!(targ->task_init->flag_features & TASK_FEATURE_ROUTING)) { + set_errf("tx port form route not supported mode %s", targ->task_init->mode_str); + return -1; + } + + if (parse_port_name_list(vals, &n_if, PROX_MAX_PORTS, pkey)) { + return -1; + } + + for (uint8_t i = 0; i < n_if; ++i) { + targ->tx_port_queue[i].port = vals[i]; + targ->nb_txports++; + } + targ->runtime_flags |= TASK_ROUTING; + return 0; + } + if (STR_EQ(str, "tx ports from cpe table")) { + uint32_t vals[PROX_MAX_PORTS]; + int n_remap = -1; + uint32_t ret; + uint32_t val; + char* mapping_str = strstr(pkey, " remap="); + + if (mapping_str != NULL) { + *mapping_str = 0; + mapping_str += strlen(" remap="); + n_remap = parse_remap(targ->mapping, mapping_str); + } + + if (parse_port_name_list(vals, &ret, PROX_MAX_PORTS, pkey)) { + return -1; + } + + if (n_remap != -1 && ret != (uint32_t)n_remap) { + set_errf("Expected %d remap elements but had %d", n_remap, ret); + return -1; + } + + for (uint8_t i = 0; i < ret; ++i) { + targ->tx_port_queue[i].port = vals[i]; + + /* default mapping this case is port0 -> port0 */ + if (n_remap == -1) { + targ->mapping[vals[i]] = i; + } + } + + targ->nb_txports = ret; + + return 0; + } + if (STR_EQ(str, "tx cores from routing table")) { + if (!(targ->task_init->flag_features & TASK_FEATURE_ROUTING)) { + set_errf("tx port form route not supported mode %s", targ->task_init->mode_str); + return -1; + } + + struct core_task_set *cts = &targ->core_task_set[0]; + + if (parse_task_set(cts, pkey)) + return -1; + + if (cts->n_elems > MAX_WT_PER_LB) { + set_errf("Maximum worker threads allowed is %u but have %u", MAX_WT_PER_LB, cts->n_elems); + return -1; + } + + targ->nb_worker_threads = cts->n_elems; + targ->nb_txrings = cts->n_elems; + + if (targ->nb_txrings > MAX_RINGS_PER_TASK) { + set_errf("Maximum allowed TX rings is %u but have %u", MAX_RINGS_PER_TASK, targ->nb_txrings); + return -1; + } + + targ->runtime_flags |= TASK_ROUTING; + return 0; + } + if (STR_EQ(str, "tx cores from cpe table")) { + struct core_task_set *core_task_set = &targ->core_task_set[0]; + int ret, ret2; + char *mapping_str; + + mapping_str = strstr(pkey, " remap="); + if (mapping_str == NULL) { + set_errf("There is no default mapping for tx cores from cpe table. Please specify it through remap="); + return -1; + } + *mapping_str = 0; + mapping_str += strlen(" remap="); + ret = parse_remap(targ->mapping, mapping_str); + if (ret <= 0) { + return -1; + } + + struct core_task_set *cts = &targ->core_task_set[0]; + + if (parse_task_set(cts, pkey)) + return -1; + if (cts->n_elems > MAX_RINGS_PER_TASK) { + set_errf("Maximum cores to route to is %u\n", MAX_RINGS_PER_TASK); + return -1; + } + + targ->nb_txrings = cts->n_elems; + + if (ret != targ->nb_txrings) { + set_errf("Expecting same number of remaps as cores\n", str); + return -1; + } + return 0; + } + + if (STR_EQ(str, "delay ms")) { + if (targ->delay_us) { + set_errf("delay ms and delay us are mutually exclusive\n", str); + return -1; + } + uint32_t delay_ms; + int rc = parse_int(&delay_ms, pkey); + targ->delay_us = delay_ms * 1000; + return rc; + } + if (STR_EQ(str, "delay us")) { + if (targ->delay_us) { + set_errf("delay ms and delay us are mutually exclusive\n", str); + return -1; + } + return parse_int(&targ->delay_us, pkey); + } + if (STR_EQ(str, "random delay us")) { + return parse_int(&targ->random_delay_us, pkey); + } + if (STR_EQ(str, "cpe table timeout ms")) { + return parse_int(&targ->cpe_table_timeout_ms, pkey); + } + if (STR_EQ(str, "ctrl path polling frequency")) { + int rc = parse_int(&targ->ctrl_freq, pkey); + if (rc == 0) { + if (targ->ctrl_freq == 0) { + set_errf("ctrl frequency must be non null."); + return -1; + } + } + return rc; + } + + if (STR_EQ(str, "handle arp")) { + return parse_flag(&targ->runtime_flags, TASK_CTRL_HANDLE_ARP, pkey); + } + if (STR_EQ(str, "fast path handle arp")) { + return parse_flag(&targ->runtime_flags, TASK_FP_HANDLE_ARP, pkey); + } + if (STR_EQ(str, "multiple arp")) { + return parse_flag(&targ->flags, TASK_MULTIPLE_MAC, pkey); + } + + /* Using tx port name, only a _single_ port can be assigned to a task. */ + if (STR_EQ(str, "tx port")) { + if (targ->nb_txports > 0) { + set_errf("Only one tx port can be defined per task. Use a LB task or routing instead."); + return -1; + } + + uint32_t n_if = 0; + uint32_t ports[PROX_MAX_PORTS]; + + if(parse_port_name_list(ports, &n_if, PROX_MAX_PORTS, pkey)) { + return -1; + } + + PROX_ASSERT(n_if-1 < PROX_MAX_PORTS); + + for (uint8_t i = 0; i < n_if; ++i) { + targ->tx_port_queue[i].port = ports[i]; + targ->nb_txports++; + } + + if (n_if > 1) { + targ->nb_worker_threads = targ->nb_txports; + } + + return 0; + } + if (STR_EQ(str, "rx ring")) { + uint32_t val; + int err = parse_bool(&val, pkey); + if (!err && val && targ->rx_port_queue[0].port != OUT_DISCARD) { + set_errf("Can't read both from internal ring and external port from the same task. Use multiple tasks instead."); + return -1; + } + + return parse_flag(&targ->flags, TASK_ARG_RX_RING, pkey); + } + if (STR_EQ(str, "private")) { + return parse_bool(&targ->use_src, pkey); + } + if (STR_EQ(str, "use src ip")) { + return parse_bool(&targ->use_src, pkey); + } + if (STR_EQ(str, "nat table")) { + return parse_str(targ->nat_table, pkey, sizeof(targ->nat_table)); + } + if (STR_EQ(str, "rules")) { + return parse_str(targ->rules, pkey, sizeof(targ->rules)); + } + if (STR_EQ(str, "route table")) { + return parse_str(targ->route_table, pkey, sizeof(targ->route_table)); + } + if (STR_EQ(str, "dscp")) { + return parse_str(targ->dscp, pkey, sizeof(targ->dscp)); + } + if (STR_EQ(str, "tun_bindings")) { + return parse_str(targ->tun_bindings, pkey, sizeof(targ->tun_bindings)); + } + if (STR_EQ(str, "cpe table")) { + return parse_str(targ->cpe_table_name, pkey, sizeof(targ->cpe_table_name)); + } + if (STR_EQ(str, "user table")) { + return parse_str(targ->user_table, pkey, sizeof(targ->user_table)); + } + if (STR_EQ(str, "streams")) { + return parse_str(targ->streams, pkey, sizeof(targ->streams)); + } + if (STR_EQ(str, "local lpm")) { + return parse_flag(&targ->flags, TASK_ARG_LOCAL_LPM, pkey); + } + if (STR_EQ(str, "drop")) { + return parse_flag(&targ->flags, TASK_ARG_DROP, pkey); + } + if (STR_EQ(str, "loop")) { + parse_flag(&targ->loop, 1, pkey); + return parse_flag(&targ->loop, 1, pkey); + } + if (STR_EQ(str, "qinq")) { + return parse_flag(&targ->flags, TASK_ARG_QINQ_ACL, pkey); + } + if (STR_EQ(str, "bps")) { + return parse_u64(&targ->rate_bps, pkey); + } + if (STR_EQ(str, "random")) { + return parse_str(targ->rand_str[targ->n_rand_str++], pkey, sizeof(targ->rand_str[0])); + } + if (STR_EQ(str, "rand_offset")) { + if (targ->n_rand_str == 0) { + set_errf("No random defined previously (use random=...)"); + return -1; + } + + return parse_int(&targ->rand_offset[targ->n_rand_str - 1], pkey); + } + if (STR_EQ(str, "keep src mac")) { + return parse_flag(&targ->flags, DSF_KEEP_SRC_MAC, pkey); + } + if (STR_EQ(str, "pcap file")) { + return parse_str(targ->pcap_file, pkey, sizeof(targ->pcap_file)); + } + if (STR_EQ(str, "pkt inline")) { + char pkey2[MAX_CFG_STRING_LEN]; + if (parse_str(pkey2, pkey, sizeof(pkey2)) != 0) { + set_errf("Error while parsing pkt line, too long\n"); + return -1; + } + + const size_t pkey_len = strlen(pkey2); + targ->pkt_size = 0; + + for (size_t i = 0; i < pkey_len; ++i) { + if (pkey2[i] == ' ') + continue; + + if (i + 1 == pkey_len) { + set_errf("Incomplete byte at character %z", i); + return -1; + } + + uint8_t byte = 0; + + if (pkey2[i] >= '0' && pkey2[i] <= '9') { + byte = (pkey2[i] - '0') << 4; + } + else if (pkey2[i] >= 'a' && pkey2[i] <= 'f') { + byte = (pkey2[i] - 'a' + 10) << 4; + } + else if (pkey2[i] >= 'A' && pkey2[i] <= 'F') { + byte = (pkey2[i] - 'A' + 10) << 4; + } + else { + set_errf("Invalid character in pkt inline at byte %d (%c)", i, pkey2[i]); + return -1; + } + + if (pkey2[i + 1] >= '0' && pkey2[i + 1] <= '9') { + byte |= (pkey2[i + 1] - '0'); + } + else if (pkey2[i + 1] >= 'a' && pkey2[i + 1] <= 'f') { + byte |= (pkey2[i + 1] - 'a' + 10); + } + else if (pkey2[i + 1] >= 'A' && pkey2[i + 1] <= 'F') { + byte |= (pkey2[i + 1] - 'A' + 10); + } + else { + set_errf("Invalid character in pkt inline at byte %d (%c)", i, pkey2[i + 1]); + return -1; + } + if (targ->pkt_size == sizeof(targ->pkt_inline)) { + set_errf("Inline packet definition can't be longer than 1518"); + } + + targ->pkt_inline[targ->pkt_size++] = byte; + i += 1; + } + + return 0; + } + if (STR_EQ(str, "accuracy limit nsec")) { + return parse_int(&targ->accuracy_limit_nsec, pkey); + } + if (STR_EQ(str, "latency bucket size")) { + return parse_int(&targ->bucket_size, pkey); + } + if (STR_EQ(str, "latency buffer size")) { + return parse_int(&targ->latency_buffer_size, pkey); + } + if (STR_EQ(str, "accuracy pos")) { + return parse_int(&targ->accur_pos, pkey); + } + if (STR_EQ(str, "signature")) { + return parse_int(&targ->sig, pkey); + } + if (STR_EQ(str, "signature pos")) { + return parse_int(&targ->sig_pos, pkey); + } + if (STR_EQ(str, "lat pos")) { + targ->lat_enabled = 1; + return parse_int(&targ->lat_pos, pkey); + } + if (STR_EQ(str, "packet id pos")) { + return parse_int(&targ->packet_id_pos, pkey); + } + if (STR_EQ(str, "probability")) { + float probability; + int rc = parse_float(&probability, pkey); + if (probability == 0) { + set_errf("Probability must be != 0\n"); + return -1; + } else if (probability > 100.0) { + set_errf("Probability must be < 100\n"); + return -1; + } + targ->probability = probability * 10000; + return rc; + } + if (STR_EQ(str, "concur conn")) { + return parse_int(&targ->n_concur_conn, pkey); + } + if (STR_EQ(str, "max setup rate")) { + return parse_int(&targ->max_setup_rate, pkey); + } + if (STR_EQ(str, "pkt size")) { + return parse_int(&targ->pkt_size, pkey); + } + if (STR_EQ(str, "min bulk size")) { + return parse_int(&targ->min_bulk_size, pkey); + } + if (STR_EQ(str, "max bulk size")) { + return parse_int(&targ->max_bulk_size, pkey); + } + if (STR_EQ(str, "rx port")) { + if (targ->flags & TASK_ARG_RX_RING) { + set_errf("Can't read both from internal ring and external port from the same task. Use multiple tasks instead."); + return -1; + } + uint32_t vals[PROX_MAX_PORTS]; + uint32_t n_if; + + if (parse_port_name_list(vals, &n_if, PROX_MAX_PORTS, pkey)) { + return -1; + } + + for (uint8_t i = 0; i < n_if; ++i) { + PROX_ASSERT(vals[i] < PROX_MAX_PORTS); + targ->rx_port_queue[i].port = vals[i]; + targ->nb_rxports++; + } + return 0; + } + + if (STR_EQ(str, "mode")) { + /* Check deprecated task modes */ + char mode[255]; + int ret = parse_str(mode, pkey, sizeof(mode)); + if (ret) + return ret; + + for (uint32_t i = 0; i < RTE_DIM(task_cfg_depr); ++i) { + if (STR_EQ(mode, task_cfg_depr[i].opt)) { + set_errf("Task mode '%s' is deprecated%s%s", + task_cfg_depr[i].opt, strlen(task_cfg_depr[i].info)? ": ": "", task_cfg_depr[i].info); + return -1; + } + } + + /* master is a special mode that is always needed (cannot be turned off) */ + if (STR_EQ(mode, "master")) { + prox_cfg.master = ncore; + targ->mode = MASTER; + if (lconf->n_tasks_all > 1 || targ->task != 0) { + set_errf("Master core can only have one task\n"); + return -1; + } + return 0; + } + + struct task_init* task_init = to_task_init(mode, ""); + if (task_init) { + targ->mode = task_init->mode; + } + else { + set_errf("Task mode '%s' is invalid", mode); + tasks_list(); + return -1; + } + targ->task_init = task_init; + return 0; + } + if (STR_EQ(str, "users")) { + return parse_int(&targ->n_flows, pkey); + } + + if (STR_EQ(str, "mark")) { + return parse_flag(&targ->runtime_flags, TASK_MARK, pkey); + } + + if (STR_EQ(str, "mark green")) { + return parse_int(&targ->marking[0], pkey); + } + + if (STR_EQ(str, "mark yellow")) { + return parse_int(&targ->marking[1], pkey); + } + + if (STR_EQ(str, "mark red")) { + return parse_int(&targ->marking[2], pkey); + } + + if (STR_EQ(str, "tx cores")) { + uint8_t dest_task = 0; + /* if user did not specify, dest_port is left at default (first type) */ + uint8_t dest_proto = 0; + uint8_t ctrl = CTRL_TYPE_DP; + char *task_str = strstr(pkey, "proto="); + if (task_str) { + task_str += strlen("proto="); + + if (STR_EQ(task_str, "ipv4")) { + dest_proto = IPV4; + } + else if (STR_EQ(task_str, "arp")) { + dest_proto = ARP; + } + else if (STR_EQ(task_str, "ipv6")) { + dest_proto = IPV6; + } + else { + set_errf("proto needs to be either ipv4, arp or ipv6"); + return -1; + } + + } + + task_str = strstr(pkey, "task="); + + if (task_str) { + --task_str; + *task_str = 0; + task_str++; + task_str += strlen("task="); + char *task_str_end = strstr(task_str, " "); + if (task_str_end) { + *task_str_end = 0; + } + if (0 == strlen(task_str)) { + set_errf("Invalid task= syntax"); + return -1; + } + + switch (task_str[strlen(task_str) - 1]) { + case 'p': + ctrl = CTRL_TYPE_PKT; + break; + case 'm': + ctrl = CTRL_TYPE_MSG; + break; + case '\n': + case 0: + break; + default: + if (task_str[strlen(task_str) -1] < '0' || + task_str[strlen(task_str) -1] > '9') { + set_errf("Unknown ring type %c.\n", + task_str[strlen(task_str) - 1]); + return -1; + } + } + + dest_task = atoi(task_str); + if (dest_task >= MAX_TASKS_PER_CORE) { + set_errf("Destination task too high (max allowed %d)", MAX_TASKS_PER_CORE - 1); + return -1; + } + } + else { + dest_task = 0; + } + + struct core_task_set *cts = &targ->core_task_set[dest_proto]; + + if (parse_task_set(cts, pkey)) + return -1; + + if (cts->n_elems > MAX_WT_PER_LB) { + set_errf("Too many worker threads (max allowed %d)", MAX_WT_PER_LB - 1); + return -1; + } + + targ->nb_worker_threads = cts->n_elems; + targ->nb_txrings += cts->n_elems; + + return 0; + } + if (STR_EQ(str, "tx crc")) { + return parse_flag(&targ->runtime_flags, TASK_TX_CRC, pkey); + } + if (STR_EQ(str, "ring size")) { + return parse_int(&targ->ring_size, pkey); + } + if (STR_EQ(str, "mempool size")) { + return parse_kmg(&targ->nb_mbuf, pkey); + } + + else if (STR_EQ(str, "mbuf size")) { + targ->mbuf_size_set_explicitely = 1; + return parse_int(&targ->mbuf_size, pkey); + } + if (STR_EQ(str, "memcache size")) { + return parse_kmg(&targ->nb_cache_mbuf, pkey); + } + + if (STR_EQ(str, "byte offset")) { + return parse_int(&targ->byte_offset, pkey); + } + + if (STR_EQ(str, "name")) { + return parse_str(lconf->name, pkey, sizeof(lconf->name)); + } + /* MPLS configuration */ + if (STR_EQ(str, "untag mpls")) { + return parse_flag(&targ->runtime_flags, TASK_MPLS_TAGGING, pkey); + } + + if (STR_EQ(str, "add mpls")) { + return parse_flag(&targ->runtime_flags, TASK_MPLS_TAGGING, pkey); + } + + if (STR_EQ(str, "ether type")) { + return parse_int(&targ->etype, pkey); + } + + if (STR_EQ(str, "cache set")) { + return parse_int(&lconf->cache_set, pkey); + } + + if (STR_EQ(str, "sub mode")) { + const char* mode_str = targ->task_init->mode_str; + const char *sub_mode_str = pkey; + + targ->task_init = to_task_init(mode_str, sub_mode_str); + if (!targ->task_init) { + set_errf("sub mode %s not supported for mode %s", sub_mode_str, mode_str); + return -1; + } + return 0; + } + + if (STR_EQ(str, "mempool name")) { + return parse_str(targ->pool_name, pkey, sizeof(targ->pool_name)); + } + if (STR_EQ(str, "dpi engine")) { + return parse_str(targ->dpi_engine_path, pkey, sizeof(targ->dpi_engine_path)); + } + if (STR_EQ(str, "dpi engine arg")) { + return parse_str(targ->dpi_engine_args[targ->n_dpi_engine_args++], pkey, + sizeof(targ->dpi_engine_args[0])); + } + if (STR_EQ(str, "dst mac")) { /* destination MAC address to be used for packets */ + if (parse_mac(&targ->edaddr, pkey)) { + if (STR_EQ(pkey, "no")) { + targ->flags |= TASK_ARG_DO_NOT_SET_DST_MAC; + return 0; + } + if (STR_EQ(pkey, "packet") == 0) + return -1; + else + return 0; + } + targ->flags |= TASK_ARG_DST_MAC_SET; + return 0; + } + if (STR_EQ(str, "src mac")) { + if (parse_mac(&targ->esaddr, pkey)) { + if (STR_EQ(pkey, "no")) { + targ->flags |= TASK_ARG_DO_NOT_SET_SRC_MAC; + return 0; + } + else if (STR_EQ(pkey, "packet")) + return 0; + else if (STR_EQ(pkey, "packet")) { + targ->flags |= TASK_ARG_HW_SRC_MAC; + return 0; + } else { + return -1; + } + } + targ->flags |= TASK_ARG_SRC_MAC_SET; + return 0; + } + if (STR_EQ(str, "gateway ipv4")) { /* Gateway IP address used when generating */ + return parse_ip(&targ->gateway_ipv4, pkey); + } + if (STR_EQ(str, "number of ip")) { /* Gateway IP address used when generating */ + return parse_int(&targ->number_gen_ip, pkey); + } + if (STR_EQ(str, "local ipv4")) { /* source IP address to be used for packets */ + return parse_ip(&targ->local_ipv4, pkey); + } + if (STR_EQ(str, "local ipv6")) { /* source IPv6 address to be used for packets */ + return parse_ip6(&targ->local_ipv6, pkey); + } + if (STR_EQ(str, "number of packets")) + return parse_int(&targ->n_pkts, pkey); + if (STR_EQ(str, "pipes")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) + return -1; + if (!val || !rte_is_power_of_2(val)) { + set_errf("Number of pipes has to be power of 2 and not zero"); + return -1; + } + + targ->qos_conf.port_params.n_pipes_per_subport = val; + return 0; + } + if (STR_EQ(str, "queue size")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + + targ->qos_conf.port_params.qsize[0] = val; + targ->qos_conf.port_params.qsize[1] = val; + targ->qos_conf.port_params.qsize[2] = val; + targ->qos_conf.port_params.qsize[3] = val; + return 0; + } + if (STR_EQ(str, "subport tb rate")) { + return parse_int(&targ->qos_conf.subport_params[0].tb_rate, pkey); + } + if (STR_EQ(str, "subport tb size")) { + return parse_int(&targ->qos_conf.subport_params[0].tb_size, pkey); + } + if (STR_EQ(str, "subport tc 0 rate")) { + return parse_int(&targ->qos_conf.subport_params[0].tc_rate[0], pkey); + } + if (STR_EQ(str, "subport tc 1 rate")) { + return parse_int(&targ->qos_conf.subport_params[0].tc_rate[1], pkey); + } + if (STR_EQ(str, "subport tc 2 rate")) { + return parse_int(&targ->qos_conf.subport_params[0].tc_rate[2], pkey); + } + if (STR_EQ(str, "subport tc 3 rate")) { + return parse_int(&targ->qos_conf.subport_params[0].tc_rate[3], pkey); + } + + if (STR_EQ(str, "subport tc rate")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + + targ->qos_conf.subport_params[0].tc_rate[0] = val; + targ->qos_conf.subport_params[0].tc_rate[1] = val; + targ->qos_conf.subport_params[0].tc_rate[2] = val; + targ->qos_conf.subport_params[0].tc_rate[3] = val; + + return 0; + } + if (STR_EQ(str, "subport tc period")) { + return parse_int(&targ->qos_conf.subport_params[0].tc_period, pkey); + } + if (STR_EQ(str, "pipe tb rate")) { + return parse_int(&targ->qos_conf.pipe_params[0].tb_rate, pkey); + } + if (STR_EQ(str, "pipe tb size")) { + return parse_int(&targ->qos_conf.pipe_params[0].tb_size, pkey); + } + if (STR_EQ(str, "pipe tc rate")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + + targ->qos_conf.pipe_params[0].tc_rate[0] = val; + targ->qos_conf.pipe_params[0].tc_rate[1] = val; + targ->qos_conf.pipe_params[0].tc_rate[2] = val; + targ->qos_conf.pipe_params[0].tc_rate[3] = val; + return 0; + } + if (STR_EQ(str, "pipe tc 0 rate")) { + return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[0], pkey); + } + if (STR_EQ(str, "pipe tc 1 rate")) { + return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[1], pkey); + } + if (STR_EQ(str, "pipe tc 2 rate")) { + return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[2], pkey); + } + if (STR_EQ(str, "pipe tc 3 rate")) { + return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[3], pkey); + } + if (STR_EQ(str, "pipe tc period")) { + return parse_int(&targ->qos_conf.pipe_params[0].tc_period, pkey); + } + if (STR_EQ(str, "police action")) { + char *in = strstr(pkey, " io="); + if (in == NULL) { + set_errf("Need to specify io colors using io=in_color,out_color\n"); + return -1; + } + *in = 0; + in += strlen(" io="); + + char *out = strstr(in, ","); + if (out == NULL) { + set_errf("Output color not specified\n"); + } + *out = 0; + out++; + + enum police_action in_color = str_to_color(in); + enum police_action out_color = str_to_color(out); + + if (in_color == ACT_INVALID) { + set_errf("Invalid input color %s. Expected green, yellow or red", in); + return -1; + } + if (out_color == ACT_INVALID) { + set_errf("Invalid output color %s. Expected green, yellow or red", out); + return -1; + } + enum police_action action = str_to_color(pkey); + if (action == ACT_INVALID) { + set_errf("Error action %s. Expected green, yellow, red or drop", pkey); + return -1; + } + targ->police_act[in_color][out_color] = action; + + return 0; + } + if (STR_EQ(str, "qinq tag")) { + return parse_int(&targ->qinq_tag, pkey); + } + if (STR_EQ(str, "cir")) { + return parse_int(&targ->cir, pkey); + } + if (STR_EQ(str, "cbs")) { + return parse_int(&targ->cbs, pkey); + } + if (STR_EQ(str, "pir")) { + return parse_int(&targ->pir, pkey); + } + if (STR_EQ(str, "pbs")) { + return parse_int(&targ->pbs, pkey); + } + if (STR_EQ(str, "ebs")) { + return parse_int(&targ->ebs, pkey); + } + uint32_t queue_id = 0; + if (sscanf(str, "queue %d weight", &queue_id) == 1) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + targ->qos_conf.pipe_params[0].wrr_weights[queue_id] = val; + return 0; + } + if (STR_EQ(str, "classify")) { + if (!(targ->task_init->flag_features & TASK_FEATURE_CLASSIFY)) { + set_errf("Classify is not supported in '%s' mode", targ->task_init->mode_str); + return -1; + } + + return parse_flag(&targ->runtime_flags, TASK_CLASSIFY, pkey); + } + if (STR_EQ(str, "flow table size")) { + return parse_int(&targ->flow_table_size, pkey); + } +#ifdef GRE_TP + if (STR_EQ(str, "tbf rate")) { + return parse_int(&targ->tb_rate, pkey); + } + if (STR_EQ(str, "tbf size")) { + return parse_int(&targ->tb_size, pkey); + } +#endif + if (STR_EQ(str, "max rules")) { + return parse_int(&targ->n_max_rules, pkey); + } + + if (STR_EQ(str, "tunnel hop limit")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + targ->tunnel_hop_limit = val; + return 0; + } + + if (STR_EQ(str, "lookup port mask")) { + uint32_t val; + int err = parse_int(&val, pkey); + if (err) { + return -1; + } + targ->lookup_port_mask = val; + return 0; + } + + set_errf("Option '%s' is not known", str); + /* fail on unknown keys */ + return -1; +} + +static int str_is_number(const char *in) +{ + int dot_once = 0; + + for (size_t i = 0; i < strlen(in); ++i) { + if (!dot_once && in[i] == '.') { + dot_once = 1; + continue; + } + + if (in[i] < '0' || in[i] > '9') + return 0; + } + + return 1; +} + +/* command line parameters parsing procedure */ +int prox_parse_args(int argc, char **argv) +{ + int i, opt, ret; + char *tmp, *tmp2; + char tmp3[64]; + + /* Default settings */ + prox_cfg.flags |= DSF_AUTOSTART | DSF_WAIT_ON_QUIT; + prox_cfg.ui = PROX_UI_CURSES; + + plog_info("\tCommand line:"); + for (i = 0; i < argc; ++i) { + plog_info(" %s", argv[i]); + } + plog_info("\n"); + + while ((opt = getopt(argc, argv, "f:dnzpo:tkuar:emsiw:l:v:q:")) != EOF) { + switch (opt) { + case 'f': + /* path to config file */ + cfg_file = optarg; + size_t offset = 0; + for (size_t i = 0; i < strlen(cfg_file); ++i) { + if (cfg_file[i] == '/') { + offset = i + 1; + } + } + + strncpy(prox_cfg.name, cfg_file + offset, MAX_NAME_SIZE); + break; + case 'v': + plog_set_lvl(atoi(optarg)); + break; + case 'l': + prox_cfg.log_name_pid = 0; + strncpy(prox_cfg.log_name, optarg, MAX_NAME_SIZE); + break; + case 'p': + prox_cfg.log_name_pid = 1; + break; + case 'k': + prox_cfg.use_stats_logger = 1; + break; + case 'd': + prox_cfg.flags |= DSF_DAEMON; + prox_cfg.ui = PROX_UI_NONE; + break; + case 'z': + prox_cfg.flags |= DSF_USE_DUMMY_CPU_TOPO; + prox_cfg.flags |= DSF_CHECK_INIT; + break; + case 'n': + prox_cfg.flags |= DSF_USE_DUMMY_DEVICES; + break; + case 'r': + if (!str_is_number(optarg) || strlen(optarg) > 11) + return -1; + strncpy(prox_cfg.update_interval_str, optarg, sizeof(prox_cfg.update_interval_str)); + break; + case 'o': + if (prox_cfg.flags & DSF_DAEMON) + break; + + if (!strcmp(optarg, "curses")) { + prox_cfg.ui = PROX_UI_CURSES; + } + else if (!strcmp(optarg, "cli")) { + prox_cfg.ui = PROX_UI_CLI; + } + else if (!strcmp(optarg, "none")) { + prox_cfg.ui = PROX_UI_NONE; + } + else { + plog_err("Invalid local UI '%s', local UI can be 'curses', 'cli' or 'none'.", optarg); + return -1; + } + break; + case 'q': + if (luaL_loadstring(prox_lua(), optarg)) { + set_errf("Lua error: '%s'\n", lua_tostring(prox_lua(), -1)); + return -1; + } + + if (lua_pcall(prox_lua(), 0, LUA_MULTRET, 0)) { + set_errf("Lua error: '%s'\n", lua_tostring(prox_lua(), -1)); + return -1; + } + + break; + case 'a': + /* autostart all cores */ + prox_cfg.flags |= DSF_AUTOSTART; + break; + case 'e': + /* don't autostart */ + prox_cfg.flags &= ~DSF_AUTOSTART; + break; + case 't': + prox_cfg.flags |= DSF_LISTEN_TCP; + break; + case 'u': + prox_cfg.flags |= DSF_LISTEN_UDS; + break; + case 'm': + /* list supported task modes and exit */ + prox_cfg.flags |= DSF_LIST_TASK_MODES; + break; + case 's': + /* check configuration file syntax and exit */ + prox_cfg.flags |= DSF_CHECK_SYNTAX; + break; + case 'i': + /* check initialization sequence and exit */ + prox_cfg.flags |= DSF_CHECK_INIT; + break; + case 'w': + tmp = optarg; + tmp2 = 0; + if (strlen(tmp) >= 3 && + (tmp2 = strchr(tmp, '='))) { + *tmp2 = 0; + tmp3[0] = '$'; + strncpy(tmp3 + 1, tmp, 63); + plog_info("\tAdding variable: %s = %s\n", tmp3, tmp2 + 1); + ret = add_var(tmp3, tmp2 + 1, 1); + if (ret == -2) { + plog_err("\tFailed to add variable, too many variables defines\n"); + return -1; + } + else if(ret == -3) { + plog_err("\tFailed to add variable, already defined\n"); + return -1; + } + break; + } + /* fall-through */ + default: + plog_err("\tUnknown option\n"); + return -1; + } + } + + /* reset getopt lib for DPDK */ + optind = 0; + + return 0; +} + +static int check_cfg(void) +{ + /* Sanity check */ +#define RETURN_IF(cond, err) \ + if (cond) { \ + plog_err(err); \ + return -1; \ + }; + + RETURN_IF(rte_cfg.force_nchannel == 0, "\tError: number of memory channels not specified in [eal options] section\n"); + RETURN_IF(prox_cfg.master >= RTE_MAX_LCORE, "\tError: No master core specified (one core needs to have mode=master)\n"); + +#undef RETURN_IF + + return 0; +} + +static int calc_tot_rxrings(void) +{ + struct lcore_cfg *slconf, *dlconf; + struct task_args *starg, *dtarg; + uint32_t dlcore_id; + uint8_t dtask_id; + struct core_task ct; + + dlconf = NULL; + while (core_targ_next_early(&dlconf, &dtarg, 1) == 0) { + dtarg->tot_rxrings = 0; + } + + slconf = NULL; + while (core_targ_next_early(&slconf, &starg, 1) == 0) { + for (uint8_t idx = 0; idx < MAX_PROTOCOLS; ++idx) { + for (uint8_t ring_idx = 0; ring_idx < starg->core_task_set[idx].n_elems; ++ring_idx) { + ct = starg->core_task_set[idx].core_task[ring_idx]; + if (!prox_core_active(ct.core, 0)) { + set_errf("Core %u is disabled but Core %u task %u is sending to it\n", + ct.core, slconf->id, starg->id); + return -1; + } + + dlconf = &lcore_cfg_init[ct.core]; + + if (ct.task >= dlconf->n_tasks_all) { + set_errf("Core %u task %u not enabled\n", ct.core, ct.task); + return -1; + } + + dtarg = &dlconf->targs[ct.task]; + + /* Control rings are not relevant at this point. */ + if (ct.type) + continue; + + if (!(dtarg->flags & TASK_ARG_RX_RING)) { + set_errf("Core %u task %u is not expecting to receive through a ring\n", + ct.core, ct.task); + return -1; + } + + dtarg->tot_rxrings++; + if (dtarg->tot_rxrings > MAX_RINGS_PER_TASK) { + set_errf("Core %u task %u is receiving from too many tasks", + ct.core, ct.task); + return -1; + } + } + } + } + + return 0; +} + +static void prox_set_core_mask(void) +{ + struct lcore_cfg *lconf; + + prox_core_clr(); + for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + lconf = &lcore_cfg_init[lcore_id]; + if (lconf->n_tasks_all > 0 && lconf->targs[0].mode != MASTER) { + prox_core_set_active(lcore_id); + } + } +} + +static int is_using_no_drop(void) +{ + uint32_t lcore_id; + struct lcore_cfg *lconf; + struct task_args *targs; + + lcore_id = -1; + while(prox_core_next(&lcore_id, 1) == 0) { + lconf = &lcore_cfg_init[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + targs = &lconf->targs[task_id]; + if (!(targs->flags & TASK_ARG_DROP)) + return 1; + } + } + return 0; +} + +int prox_read_config_file(void) +{ + set_global_defaults(&prox_cfg); + set_task_defaults(&prox_cfg, lcore_cfg_init); + set_port_defaults(); + plog_info("=== Parsing configuration file '%s' ===\n", cfg_file); + struct cfg_file *pcfg = cfg_open(cfg_file); + if (pcfg == NULL) { + return -1; + } + + struct cfg_section* config_sections[] = { + &lua_cfg , + &var_cfg , + &eal_default_cfg , + &cache_set_cfg , + &port_cfg , + &defaults_cfg , + &settings_cfg , + &core_cfg , + NULL + }; + + for (struct cfg_section** section = config_sections; *section != NULL; ++section) { + const char* name = (*section)->name; + size_t len = strlen(name); + plog_info("\t*** Reading [%s] section%s ***\n", name, name[len - 1] == '#'? "s": ""); + cfg_parse(pcfg, *section); + + if ((*section)->error) { + plog_err("At line %u, section [%s], entry %u: '%s'\n\t%s\n" + , pcfg->err_line, pcfg->err_section, pcfg->err_entry + 1, pcfg->cur_line, + strlen(get_parse_err())? get_parse_err() : err_str); + cfg_close(pcfg); /* cannot close before printing error, print uses internal buffer */ + return -1; + } + } + + cfg_close(pcfg); + + prox_set_core_mask(); + + if (is_using_no_drop()) { + prox_cfg.flags &= ~DSF_WAIT_ON_QUIT; + } + + if (calc_tot_rxrings()) { + plog_err("Error in configuration: %s\n", err_str); + return -1; + } + + return check_cfg(); +} + +static void failed_rte_eal_init(__attribute__((unused))const char *prog_name) +{ + plog_err("\tError in rte_eal_init()\n"); +} + +int prox_setup_rte(const char *prog_name) +{ + char *rte_argv[MAX_RTE_ARGV]; + char rte_arg[MAX_RTE_ARGV][MAX_ARG_LEN]; + char tmp[PROX_CM_STR_LEN]; + /* create mask of used cores */ + plog_info("=== Setting up RTE EAL ===\n"); + + if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) { + plog_info("Using dummy cpu topology\n"); + snprintf(tmp, sizeof(tmp), "0x1"); + } else { + prox_core_to_hex(tmp, sizeof(tmp), 0); + plog_info("\tWorker threads core mask is %s\n", tmp); + prox_core_to_hex(tmp, sizeof(tmp), 1); + plog_info("\tWith master core index %u, full core mask is %s\n", prox_cfg.master, tmp); + } + + /* fake command line parameters for rte_eal_init() */ + int argc = 0; + rte_argv[argc] = strdup(prog_name); + sprintf(rte_arg[++argc], "-c%s", tmp); + rte_argv[argc] = rte_arg[argc]; +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) + sprintf(rte_arg[++argc], "--master-lcore=%u", 0); + else + sprintf(rte_arg[++argc], "--master-lcore=%u", prox_cfg.master); + rte_argv[argc] = rte_arg[argc]; +#else + /* For old DPDK versions, the master core had to be the first + core. */ + uint32_t first_core = -1; + + if (prox_core_next(&first_core, 1) == -1) { + plog_err("Can't core ID of first core in use\n"); + return -1; + } + if (first_core != prox_cfg.master) { + plog_err("The master core needs to be the first core (master core = %u, first core = %u).\n", first_core, prox_cfg.master); + return -1; + } +#endif + + if (rte_cfg.memory) { + sprintf(rte_arg[++argc], "-m%u", rte_cfg.memory); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.force_nchannel) { + sprintf(rte_arg[++argc], "-n%u", rte_cfg.force_nchannel); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.force_nrank) { + sprintf(rte_arg[++argc], "-r%u", rte_cfg.force_nrank); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.no_hugetlbfs) { + strcpy(rte_arg[++argc], "--no-huge"); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.no_pci) { + strcpy(rte_arg[++argc], "--no-pci"); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.no_hpet) { + strcpy(rte_arg[++argc], "--no-hpet"); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.no_shconf) { + strcpy(rte_arg[++argc], "--no-shconf"); + rte_argv[argc] = rte_arg[argc]; + } + + if (rte_cfg.eal != NULL) { + char *ptr = rte_cfg.eal; + char *ptr2; + while (ptr != NULL) { + while (isspace(*ptr)) + ptr++; + ptr2 = ptr; + ptr = strchr(ptr, ' '); + if (ptr) { + *ptr++ = '\0'; + } + strcpy(rte_arg[++argc], ptr2); + rte_argv[argc] = rte_arg[argc]; + } + } + + if (rte_cfg.hugedir != NULL) { + strcpy(rte_arg[++argc], "--huge-dir"); + rte_argv[argc] = rte_arg[argc]; + rte_argv[++argc] = rte_cfg.hugedir; + } + + if (rte_cfg.no_output) { + rte_set_log_level(0); + } + /* init EAL */ + plog_info("\tEAL command line:"); + if (argc >= MAX_RTE_ARGV) { + plog_err("too many arguments for EAL\n"); + return -1; + } + + for (int h = 0; h <= argc; ++h) { + plog_info(" %s", rte_argv[h]); + } + plog_info("\n"); + + rte_set_application_usage_hook(failed_rte_eal_init); + if (rte_eal_init(++argc, rte_argv) < 0) { + plog_err("\tError in rte_eal_init()\n"); + return -1; + } + plog_info("\tEAL Initialized\n"); + + if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) + return 0; + + /* check if all active cores are in enabled in DPDK */ + for (uint32_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { + if (lcore_id == prox_cfg.master) { + if (!rte_lcore_is_enabled(lcore_id)) + return -1; + } + else if (rte_lcore_is_enabled(lcore_id) != prox_core_active(lcore_id, 0)) { + plog_err("\tFailed to enable lcore %u\n", lcore_id); + return -1; + } + else if (lcore_cfg_init[lcore_id].n_tasks_all != 0 && !rte_lcore_is_enabled(lcore_id)) { + plog_err("\tFailed to enable lcore %u\n", lcore_id); + return -1; + } + } + return 0; +} diff --git a/VNFs/DPPD-PROX/prox_args.h b/VNFs/DPPD-PROX/prox_args.h new file mode 100644 index 00000000..1c900054 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_args.h @@ -0,0 +1,41 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_ARGS_H_ +#define _PROX_ARGS_H_ + +#include "lconf.h" + +struct rte_cfg { + /* DPDK standard options */ + uint32_t memory; /* amount of asked memory */ + uint32_t force_nchannel; /* force number of channels */ + uint32_t force_nrank; /* force number of ranks */ + uint32_t no_hugetlbfs; /* true to disable hugetlbfs */ + uint32_t no_pci; /* true to disable PCI */ + uint32_t no_hpet; /* true to disable HPET */ + uint32_t no_shconf; /* true if there is no shared config */ + char *hugedir; /* dir where hugetlbfs is mounted */ + char *eal; /* any additional eal option */ + uint32_t no_output; /* disable EAL debug output */ +}; + +int prox_parse_args(int argc, char **argv); +int prox_read_config_file(void); +int prox_setup_rte(const char *prog_name); +const char *get_cfg_dir(void); + +#endif /* _PROX_ARGS_H_ */ diff --git a/VNFs/DPPD-PROX/prox_assert.h b/VNFs/DPPD-PROX/prox_assert.h new file mode 100644 index 00000000..cc4f24e6 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_assert.h @@ -0,0 +1,39 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_ASSERT_H_ +#define _PROX_ASSERT_H_ + +#include <assert.h> +#include "display.h" + +#if defined(__KLOCWORK__) || defined(ASSERT) + +#ifdef NDEBUG +#error When enabling asserts, NDEBUG must be undefined +#endif + +#define PROX_ASSERT(cond) do { \ + if (!(cond)) { \ + display_end(); \ + assert(cond); \ + } \ + } while (0) +#else +#define PROX_ASSERT(cond) do {} while(0) +#endif + +#endif /* _PROX_ASSERT_H_ */ diff --git a/VNFs/DPPD-PROX/prox_cfg.c b/VNFs/DPPD-PROX/prox_cfg.c new file mode 100644 index 00000000..a2cf7953 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_cfg.c @@ -0,0 +1,145 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> + +#include "prox_cfg.h" + +#define CM_N_BITS (sizeof(prox_cfg.core_mask[0]) * 8) +#define CM_ALL_N_BITS (sizeof(prox_cfg.core_mask) * 8) + +struct prox_cfg prox_cfg = { + .update_interval_str = "1" +}; + +static int prox_cm_isset(const uint32_t lcore_id) +{ + uint64_t cm; + uint32_t cm_idx; + + if (lcore_id > CM_ALL_N_BITS) + return -1; + + cm = __UINT64_C(1) << (lcore_id % CM_N_BITS); + cm_idx = PROX_CM_DIM - 1 - lcore_id / CM_N_BITS; + return !!(prox_cfg.core_mask[cm_idx] & cm); +} + +int prox_core_active(const uint32_t lcore_id, const int with_master) +{ + int ret; + + ret = prox_cm_isset(lcore_id); + if (ret < 0) + return 0; + + if (with_master) + return ret || lcore_id == prox_cfg.master; + else + return ret && lcore_id != prox_cfg.master; +} + +int prox_core_next(uint32_t* lcore_id, const int with_master) +{ + for (uint32_t i = *lcore_id + 1; i < CM_ALL_N_BITS; ++i) { + if (prox_core_active(i, with_master)) { + *lcore_id = i; + return 0; + } + } + return -1; +} + +int prox_core_to_hex(char *dst, const size_t size, const int with_master) +{ + uint64_t cm; + uint32_t cm_len; + uint32_t cm_first = 0; + uint32_t master = prox_cfg.master; + + /* Minimum size of the string has to big enough to hold the + bitmask in hex (including the prefix "0x"). */ + if (size < PROX_CM_STR_LEN) + return 0; + + snprintf(dst, size, "0x"); + for (uint32_t i = 0; i < PROX_CM_DIM; ++i, cm_first = i) { + if ((with_master && ((CM_ALL_N_BITS - 1 - master) / CM_N_BITS == i * CM_N_BITS)) || + prox_cfg.core_mask[i]) { + break; + } + } + + for (uint32_t i = cm_first; i < PROX_CM_DIM; ++i) { + cm = prox_cfg.core_mask[i]; + if (with_master && ((CM_ALL_N_BITS - 1 - master) / CM_N_BITS == i)) { + cm |= (__UINT64_C(1) << (master % CM_N_BITS)); + } + + snprintf(dst + strlen(dst), size - strlen(dst), i == cm_first? "%lx" : "%016lx", cm); + } + + return 0; +} + +int prox_core_to_str(char *dst, const size_t size, const int with_master) +{ + uint32_t lcore_id = -1; + uint32_t first = 1; + + *dst = 0; + lcore_id - 1; + while (prox_core_next(&lcore_id, with_master) == 0) { + /* Stop printing to string if there is not engough + space left. Assume that adding 1 core to the string + will take at most 5 + 1 bytes implying that + lcore_id < 999. Check if ther is space for another + 6 bytes to add an elipsis */ + if (12 + strlen(dst) > size) { + if (6 + strlen(dst) > size) { + snprintf(dst + strlen(dst), size - strlen(dst), ", ..."); + return 0; + } + return -1; + } + + snprintf(dst + strlen(dst), size - strlen(dst), first? "%u" : ", %u", lcore_id); + first = 0; + } + + return 0; +} + +void prox_core_clr(void) +{ + memset(prox_cfg.core_mask, 0, sizeof(prox_cfg.core_mask)); +} + +int prox_core_set_active(const uint32_t lcore_id) +{ + uint32_t cm_idx; + uint64_t cm; + + if (lcore_id > CM_ALL_N_BITS) + return -1; + + cm = __UINT64_C(1) << (lcore_id % CM_N_BITS); + cm_idx = PROX_CM_DIM - 1 - lcore_id / CM_N_BITS; + prox_cfg.core_mask[cm_idx] |= cm; + + return 0; +} diff --git a/VNFs/DPPD-PROX/prox_cfg.h b/VNFs/DPPD-PROX/prox_cfg.h new file mode 100644 index 00000000..a7d0e7ea --- /dev/null +++ b/VNFs/DPPD-PROX/prox_cfg.h @@ -0,0 +1,87 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_CFG_H +#define _PROX_CFG_H + +#include <inttypes.h> + +#include "prox_globals.h" + +#define PROX_CM_STR_LEN (2 + 2 * sizeof(prox_cfg.core_mask) + 1) +#define PROX_CM_DIM (RTE_MAX_LCORE/(sizeof(uint64_t) * 8)) + +#define DSF_AUTOSTART 0x00000001 /* start all cores automatically */ +#define DSF_CHECK_INIT 0x00000002 /* check initialization sequence and exit */ +#define DSF_CHECK_SYNTAX 0x00000004 /* check configuration file syntax and exit */ +#define DSF_SHUFFLE 0x00000008 /* shuffle memory addresses within memory pool */ +#define DSF_WAIT_ON_QUIT 0x00000010 /* wait for all cores to stop before exiting */ +#define DSF_LISTEN_TCP 0x00000020 /* Listen on TCP port 8474 for input */ +#define DSF_LISTEN_UDS 0x00000040 /* Listen on /tmp/prox.sock for input */ +#define DSF_DAEMON 0x00000080 /* Run process as Daemon */ +#define UNIQUE_MEMPOOL_PER_SOCKET 0x00000100 /* Use Only one mempool per socket, shared between all cores on that socket */ +#define DSF_KEEP_SRC_MAC 0x00000200 /* In gen mode, do not overwrite src_mac by mac of physical port */ +#define DSF_MP_RINGS 0x00000400 /* Use Multi Producer rings when possible */ +#define DSF_USE_DUMMY_DEVICES 0x00000800 /* Instead of relying on real PCI devices, create null devices instead */ +#define DSF_USE_DUMMY_CPU_TOPO 0x00001000 /* Instead of relying on the cpu topology, load a cpu toplogy that will work with all cfgs. */ +#define DSF_DISABLE_CMT 0x00002000 /* CMT disabled */ +#define DSF_LIST_TASK_MODES 0x00004000 /* list supported task modes and exit */ +#define DSF_ENABLE_BYPASS 0x00008000 /* Use Multi Producer rings to enable ring bypass */ + +#define MAX_PATH_LEN 1024 + +enum prox_ui { + PROX_UI_CURSES, + PROX_UI_CLI, + PROX_UI_NONE, +}; + +struct prox_cfg { + enum prox_ui ui; /* By default, curses is used as a UI. */ + char update_interval_str[16]; + int use_stats_logger; + uint32_t flags; /* TGSF_* flags above */ + uint32_t master; /* master core to run user interface on */ + uint64_t core_mask[PROX_CM_DIM]; /* Active cores without master core */ + uint32_t start_time; /* if set (not 0), average pps will be calculated starting after start_time seconds */ + uint32_t duration_time; /* if set (not 0), prox will exit duration_time seconds after start_time */ + char name[MAX_NAME_SIZE]; + uint8_t log_name_pid; + char log_name[MAX_PATH_LEN]; + int32_t cpe_table_ports[PROX_MAX_PORTS]; + uint32_t logbuf_size; + uint32_t logbuf_pos; + char *logbuf; +}; + +extern struct prox_cfg prox_cfg; + +int prox_core_active(const uint32_t lcore_id, const int with_master); + +/* Returns non-zero if supplied lcore_id is the last active core. The + first core can be found by setting *lcore_id == -1. The function is + indented to be used as an interator. */ +int prox_core_next(uint32_t *lcore_id, const int with_master); + +int prox_core_to_hex(char *dst, const size_t size, const int with_master); + +int prox_core_to_str(char *dst, const size_t size, const int with_master); + +void prox_core_clr(void); + +int prox_core_set_active(const uint32_t lcore_id); + +#endif /* __PROX_CFG_H_ */ diff --git a/VNFs/DPPD-PROX/prox_cksum.c b/VNFs/DPPD-PROX/prox_cksum.c new file mode 100644 index 00000000..b69c06f6 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_cksum.c @@ -0,0 +1,148 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "prox_cksum.h" +#include "prox_port_cfg.h" +#include <rte_byteorder.h> +#include "log.h" + +/* compute IP 16 bit checksum */ +void prox_ip_cksum_sw(struct ipv4_hdr *buf) +{ + const uint16_t size = sizeof(struct ipv4_hdr); + uint32_t cksum = 0; + uint32_t nb_dwords; + uint32_t tail, mask; + uint32_t *pdwd = (uint32_t *)buf; + + /* compute 16 bit checksum using hi and low parts of 32 bit integers */ + for (nb_dwords = (size >> 2); nb_dwords > 0; --nb_dwords) { + cksum += (*pdwd >> 16); + cksum += (*pdwd & 0xFFFF); + ++pdwd; + } + + /* deal with the odd byte length */ + if (size & 0x03) { + tail = *pdwd; + /* calculate mask for valid parts */ + mask = 0xFFFFFFFF << ((size & 0x03) << 3); + /* clear unused bits */ + tail &= ~mask; + + cksum += (tail >> 16) + (tail & 0xFFFF); + } + + cksum = (cksum >> 16) + (cksum & 0xFFFF); + cksum = (cksum >> 16) + (cksum & 0xFFFF); + + buf->hdr_checksum = ~((uint16_t)cksum); +} + +static uint16_t calc_pseudo_checksum(uint8_t ipproto, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr) +{ + uint32_t csum = 0; + + csum += (src_ip_addr >> 16) + (src_ip_addr & 0xFFFF); + csum += (dst_ip_addr >> 16) + (dst_ip_addr & 0xFFFF); + csum += rte_bswap16(ipproto) + rte_bswap16(len); + csum = (csum >> 16) + (csum & 0xFFFF); + return csum; +} + +static void prox_write_udp_pseudo_hdr(struct udp_hdr *udp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr) +{ + /* Note that the csum is not complemented, while the pseaudo + header checksum is calculated as "... the 16-bit one's + complement of the one's complement sum of a pseudo header + of information ...", the psuedoheader forms as a basis for + the actual checksum calculated later either in software or + hardware. */ + udp->dgram_cksum = calc_pseudo_checksum(IPPROTO_UDP, len, src_ip_addr, dst_ip_addr); +} + +static void prox_write_tcp_pseudo_hdr(struct tcp_hdr *tcp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr) +{ + tcp->cksum = calc_pseudo_checksum(IPPROTO_TCP, len, src_ip_addr, dst_ip_addr); +} + +void prox_ip_udp_cksum(struct rte_mbuf *mbuf, struct ipv4_hdr *pip, uint16_t l2_len, uint16_t l3_len, int cksum_offload) +{ + prox_ip_cksum(mbuf, pip, l2_len, l3_len, cksum_offload & IPV4_CKSUM); + +#ifndef SOFT_CRC + if (cksum_offload & UDP_CKSUM) + mbuf->ol_flags |= PKT_TX_UDP_CKSUM; +#endif + + uint32_t l4_len = rte_bswap16(pip->total_length) - l3_len; + if (pip->next_proto_id == IPPROTO_UDP) { + struct udp_hdr *udp = (struct udp_hdr *)(((uint8_t*)pip) + l3_len); +#ifndef SOFT_CRC + if (cksum_offload & UDP_CKSUM) + prox_write_udp_pseudo_hdr(udp, l4_len, pip->src_addr, pip->dst_addr); + else +#endif + prox_udp_cksum_sw(udp, l4_len, pip->src_addr, pip->dst_addr); + } else if (pip->next_proto_id == IPPROTO_TCP) { + struct tcp_hdr *tcp = (struct tcp_hdr *)(((uint8_t*)pip) + l3_len); +#ifndef SOFT_CRC + if (cksum_offload & UDP_CKSUM) + prox_write_tcp_pseudo_hdr(tcp, l4_len, pip->src_addr, pip->dst_addr); + else +#endif + prox_tcp_cksum_sw(tcp, l4_len, pip->src_addr, pip->dst_addr); + } +} + +static uint16_t checksum_byte_seq(uint16_t *buf, uint16_t len) +{ + uint32_t csum = 0; + + while (len > 1) { + csum += *buf; + while (csum >> 16) { + csum &= 0xffff; + csum +=1; + } + buf++; + len -= 2; + } + + if (len) { + csum += *(uint8_t*)buf; + while (csum >> 16) { + csum &= 0xffff; + csum +=1; + } + } + return ~csum; +} + +void prox_udp_cksum_sw(struct udp_hdr *udp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr) +{ + prox_write_udp_pseudo_hdr(udp, len, src_ip_addr, dst_ip_addr); + uint16_t csum = checksum_byte_seq((uint16_t *)udp, len); + udp->dgram_cksum = csum; +} + +void prox_tcp_cksum_sw(struct tcp_hdr *tcp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr) +{ + prox_write_tcp_pseudo_hdr(tcp, len, src_ip_addr, dst_ip_addr); + + uint16_t csum = checksum_byte_seq((uint16_t *)tcp, len); + tcp->cksum = csum; +} diff --git a/VNFs/DPPD-PROX/prox_cksum.h b/VNFs/DPPD-PROX/prox_cksum.h new file mode 100644 index 00000000..c11b17a5 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_cksum.h @@ -0,0 +1,68 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_CKSUM_H_ +#define _PROX_CKSUM_H_ + +#include <inttypes.h> +#include <string.h> +#include <stdio.h> +#include <rte_version.h> +#include <rte_ip.h> +#include <rte_udp.h> +#include <rte_tcp.h> +#include <rte_mbuf.h> + +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) +#define CALC_TX_OL(l2_len, l3_len) ((uint64_t)(l2_len) | (uint64_t)(l3_len) << 7) +#else +#define CALC_TX_OL(l2_len, l3_len) (((uint64_t)(l2_len) << 9) | (uint64_t)(l3_len)) +#endif + +static void prox_ip_cksum_hw(struct rte_mbuf *mbuf, uint16_t l2_len, uint16_t l3_len) +{ +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) + mbuf->pkt.vlan_macip.data = CALC_TX_OL(l2_len, l3_len); +#else + mbuf->tx_offload = CALC_TX_OL(l2_len, l3_len); +#endif + mbuf->ol_flags |= PKT_TX_IP_CKSUM; +} + +void prox_ip_cksum_sw(struct ipv4_hdr *buf); + +static inline void prox_ip_cksum(struct rte_mbuf *mbuf, struct ipv4_hdr *buf, uint16_t l2_len, uint16_t l3_len, int offload) +{ + buf->hdr_checksum = 0; +#ifdef SOFT_CRC + prox_ip_cksum_sw(buf); +#else + if (offload) + prox_ip_cksum_hw(mbuf, l2_len, l3_len); + else { + prox_ip_cksum_sw(buf); + /* TODO: calculate UDP checksum */ + } +#endif +} + +void prox_ip_udp_cksum(struct rte_mbuf *mbuf, struct ipv4_hdr *buf, uint16_t l2_len, uint16_t l3_len, int cksum_offload); + +/* src_ip_addr/dst_ip_addr are in network byte order */ +void prox_udp_cksum_sw(struct udp_hdr *udp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr); +void prox_tcp_cksum_sw(struct tcp_hdr *tcp, uint16_t len, uint32_t src_ip_addr, uint32_t dst_ip_addr); + +#endif /* _PROX_CKSUM_H_ */ diff --git a/VNFs/DPPD-PROX/prox_globals.h b/VNFs/DPPD-PROX/prox_globals.h new file mode 100644 index 00000000..b09f3a52 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_globals.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#define PROX_MAX_PORTS 16 +#define MAX_TASKS_PER_CORE 8 +#define MAX_SOCKETS 64 +#define MAX_NAME_SIZE 64 +#define MAX_PROTOCOLS 3 +#define MAX_RINGS_PER_TASK (MAX_WT_PER_LB*MAX_PROTOCOLS) +#define MAX_WT_PER_LB 64 diff --git a/VNFs/DPPD-PROX/prox_lua.c b/VNFs/DPPD-PROX/prox_lua.c new file mode 100644 index 00000000..b5c2fec9 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_lua.c @@ -0,0 +1,411 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdlib.h> + +#include "prox_lua.h" +#include "lua_compat.h" +#include "parse_utils.h" + +static struct lua_State *lua_instance; + +static int l_mask(lua_State *L) +{ + uint32_t val, mask; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "Expecting 2 argument and got %d\n", lua_gettop(L)); + } + if (!lua_isnumber(L, -1) || !lua_isnumber(L, -2)) { + return luaL_error(L, "Expecting (integer, integer) as arguments\n"); + } + val = lua_tonumber(L, -1); + mask = lua_tonumber(L, -2); + + lua_pushinteger(L, val & mask); + + return 1; +} + +static int l_server_content(lua_State *L) +{ + uint32_t beg, len; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "Expecting 2 argument and got %d\n", lua_gettop(L)); + } + if (!lua_isnumber(L, -1) || !lua_isnumber(L, -2)) { + return luaL_error(L, "Expecting (integer, integer) as arguments\n"); + } + len = lua_tonumber(L, -1); + beg = lua_tonumber(L, -2); + + lua_createtable(L, 0, 3); + + lua_pushinteger(L, beg); + lua_setfield(L, -2, "beg"); + lua_pushinteger(L, len); + lua_setfield(L, -2, "len"); + lua_pushinteger(L, 0); + lua_setfield(L, -2, "peer"); + + return 1; +} + +static int l_client_content(lua_State *L) +{ + uint32_t beg, len; + + if (lua_gettop(L) != 2) { + return luaL_error(L, "Expecting 2 argument and got %d\n", lua_gettop(L)); + } + if (!lua_isnumber(L, -1) || !lua_isnumber(L, -2)) { + return luaL_error(L, "Expecting (integer, integer) as arguments\n"); + } + len = lua_tonumber(L, -1); + beg = lua_tonumber(L, -2); + + lua_createtable(L, 0, 3); + + lua_pushinteger(L, beg); + lua_setfield(L, -2, "beg"); + lua_pushinteger(L, len); + lua_setfield(L, -2, "len"); + lua_pushinteger(L, 1); + lua_setfield(L, -2, "peer"); + + return 1; +} + +static int l_bin_read(lua_State *L) +{ + const char *file_name = lua_tostring(L, -1); + int beg = lua_tonumber(L, -2); + int len = lua_gettop(L) == 3? lua_tonumber(L, -3) : -1; + + if (lua_gettop(L) == 2) { + if (!lua_isnumber(L, -1) || !lua_isstring(L, -2)) { + return luaL_error(L, "Expecting (string, integer) as arguments\n"); + } + + file_name = lua_tostring(L, -2); + beg = lua_tonumber(L, -1); + len = -1; + } + else if (lua_gettop(L) == 3) { + if (!lua_isnumber(L, -1) || !lua_isnumber(L, -2) || !lua_isstring(L, 3)) { + return luaL_error(L, "Expecting (string, integer, integer) as arguments\n"); + } + + file_name = lua_tostring(L, -3); + beg = lua_tonumber(L, -2); + len = lua_tonumber(L, -1); + } + else + return luaL_error(L, "Expecting 2 or 3 arguments\n"); + + lua_createtable(L, 0, 3); + + lua_pushstring(L, file_name); + lua_setfield(L, -2, "file_name"); + lua_pushinteger(L, beg); + lua_setfield(L, -2, "beg"); + lua_pushinteger(L, len); + lua_setfield(L, -2, "len"); + + return 1; +} + +static int l_mac(lua_State *L) +{ + int mac[6]; + + if (lua_isstring(L, -1)) { + const char *arg = lua_tostring(L, -1); + char arg2[128]; + strncpy(arg2, arg, sizeof(arg2)); + + char *p = arg2; + int count = 0; + + while ((p = strchr(p, ':'))) { + count++; + p++; + } + p = arg2; + if (count != 5) + return luaL_error(L, "Invalid MAC format\n"); + + lua_createtable(L, 6, 0); + for (size_t i = 0; i < 6; ++i) { + char *n = strchr(p, ':'); + if (n) + *n = 0; + if (strlen(p) != 2) { + return luaL_error(L, "Invalid MAC format\n"); + } + + lua_pushinteger(L, strtol(p, NULL, 16)); + lua_rawseti(L, -2, i + 1); + p = n + 1; + } + return 1; + } + + return luaL_error(L, "Invalid argument\n"); +} + +static int l_ip(lua_State *L) +{ + int ip[4]; + if (lua_isnumber(L, -1)) { + uint32_t arg = lua_tointeger(L, -1); + + ip[0] = arg >> 24 & 0xff; + ip[1] = arg >> 16 & 0xff; + ip[2] = arg >> 8 & 0xff; + ip[3] = arg >> 0 & 0xff; + + lua_createtable(L, 4, 0); + for (size_t i = 0; i < 4; ++i) { + lua_pushinteger(L, ip[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + if (lua_isstring(L, -1)) { + const char *arg = lua_tostring(L, -1); + + if (sscanf(arg, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]) != 4) { + return luaL_error(L, "Invalid IP address format\n"); + } + + lua_createtable(L, 4, 0); + for (size_t i = 0; i < 4; ++i) { + lua_pushinteger(L, ip[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; + } + + return luaL_error(L, "Invalid argument\n"); +} + +static int l_ip6(lua_State *L) +{ + int ip[16]; + + if (!lua_isstring(L, -1)) { + return luaL_error(L, "Invalid argument type\n"); + } + + const char *arg = lua_tostring(L, -1); + char arg2[64]; + char *addr_parts[8]; + int n_parts = 0; + size_t str_len = strlen(arg); + int next_str = 1; + int ret; + + strncpy(arg2, arg, sizeof(arg2)); + + for (size_t i = 0; i < str_len; ++i) { + if (next_str) { + if (n_parts == 8) + return luaL_error(L, "IPv6 address can't be longer than 16 bytes\n"); + addr_parts[n_parts++] = &arg2[i]; + next_str = 0; + + } + if (arg2[i] == ':') { + arg2[i] = 0; + next_str = 1; + } + } + + int omitted = 0; + + for (int i = 0, j = 0; i < n_parts; ++i) { + if (*addr_parts[i] == 0) { + if (omitted == 0) { + return luaL_error(L, "Can omit zeros only once\n"); + } + omitted = 1; + j += 8 - n_parts; + } + else { + uint16_t w = strtoll(addr_parts[i], NULL, 16); + ip[j++] = (w >> 8) & 0xff; + ip[j++] = w & 0xff; + } + } + + lua_createtable(L, 16, 0); + for (size_t i = 0; i < 16; ++i) { + lua_pushinteger(L, ip[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +static int l_cidr(lua_State *L) +{ + const char *arg = lua_tostring(L, -1); + + char tmp[128]; + strncpy(tmp, arg, sizeof(tmp)); + + char *slash = strchr(tmp, '/'); + *slash = 0; + slash++; + + lua_createtable(L, 0, 2); + lua_pushstring(L, "ip"); + + lua_pushstring(L, tmp); + l_ip(L); + lua_remove(L, -2); + + lua_settable(L, -3); + + lua_pushstring(L, "depth"); + lua_pushinteger(L, atoi(slash)); + lua_settable(L, -3); + return 1; +} + +static int l_cidr6(lua_State *L) +{ + const char *arg = lua_tostring(L, -1); + + char tmp[128]; + strncpy(tmp, arg, sizeof(tmp)); + + char *slash = strchr(tmp, '/'); + *slash = 0; + slash++; + + lua_createtable(L, 0, 2); + lua_pushstring(L, "ip6"); + + lua_pushstring(L, tmp); + l_ip6(L); + lua_remove(L, -2); + + lua_settable(L, -3); + + lua_pushstring(L, "depth"); + lua_pushinteger(L, atoi(slash)); + lua_settable(L, -3); + return 1; +} + +static int l_val_mask(lua_State *L) +{ + if (!lua_isinteger(L, -2)) + return luaL_error(L, "Argument 1 is not an integer\n"); + if (!lua_isinteger(L, -1)) + return luaL_error(L, "Argument 2 is not an integer\n"); + + uint32_t val = lua_tointeger(L, -2); + uint32_t mask = lua_tointeger(L, -1); + + lua_createtable(L, 0, 2); + lua_pushstring(L, "val"); + lua_pushinteger(L, val); + lua_settable(L, -3); + + lua_pushstring(L, "mask"); + lua_pushinteger(L, mask); + lua_settable(L, -3); + + return 1; +} + +static int l_val_range(lua_State *L) +{ + if (!lua_isinteger(L, -2)) + return luaL_error(L, "Argument 1 is not an integer\n"); + if (!lua_isinteger(L, -1)) + return luaL_error(L, "Argument 2 is not an integer\n"); + + uint32_t beg = lua_tointeger(L, -2); + uint32_t end = lua_tointeger(L, -1); + + lua_createtable(L, 0, 2); + lua_pushstring(L, "beg"); + lua_pushinteger(L, beg); + lua_settable(L, -3); + + lua_pushstring(L, "end"); + lua_pushinteger(L, end); + lua_settable(L, -3); + + return 1; +} + +static int l_task_count(lua_State *L) +{ + struct core_task_set cts; + const char *str; + + if (!lua_isstring(L, -1)) + return luaL_error(L, "Argument 1 is not an string\n"); + str = lua_tostring(L, -1); + if (parse_task_set(&cts, str)) + return luaL_error(L, "Invalid core task set syntax\n"); + lua_pushinteger(L, cts.n_elems); + return 1; +} + +struct lua_State *prox_lua(void) +{ + if (!lua_instance) { + lua_instance = luaL_newstate(); + + luaL_openlibs(lua_instance); + + lua_pushcfunction(lua_instance, l_ip); + lua_setglobal(lua_instance, "ip"); + lua_pushcfunction(lua_instance, l_ip6); + lua_setglobal(lua_instance, "ip6"); + lua_pushcfunction(lua_instance, l_cidr); + lua_setglobal(lua_instance, "cidr"); + lua_pushcfunction(lua_instance, l_cidr6); + lua_setglobal(lua_instance, "cidr6"); + lua_pushcfunction(lua_instance, l_mac); + lua_setglobal(lua_instance, "mac"); + lua_pushcfunction(lua_instance, l_mask); + lua_setglobal(lua_instance, "mask"); + lua_pushcfunction(lua_instance, l_val_mask); + lua_setglobal(lua_instance, "val_mask"); + lua_pushcfunction(lua_instance, l_val_range); + lua_setglobal(lua_instance, "val_range"); + lua_pushcfunction(lua_instance, l_bin_read); + lua_setglobal(lua_instance, "bin_read"); + lua_pushcfunction(lua_instance, l_client_content); + lua_setglobal(lua_instance, "client_content"); + lua_pushcfunction(lua_instance, l_server_content); + lua_setglobal(lua_instance, "server_content"); + lua_pushcfunction(lua_instance, l_task_count); + lua_setglobal(lua_instance, "task_count"); + } + return lua_instance; +} diff --git a/VNFs/DPPD-PROX/prox_lua.h b/VNFs/DPPD-PROX/prox_lua.h new file mode 100644 index 00000000..8d29df69 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_lua.h @@ -0,0 +1,27 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_LUA_H_ +#define _PROX_LUA_H_ + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#include "lua_compat.h" + +struct lua_State *prox_lua(void); + +#endif /* _PROX_LUA_H_ */ diff --git a/VNFs/DPPD-PROX/prox_lua_types.c b/VNFs/DPPD-PROX/prox_lua_types.c new file mode 100644 index 00000000..7a0b6e08 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_lua_types.c @@ -0,0 +1,1156 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +#include <string.h> +#include <rte_ether.h> +#include <rte_lpm.h> +#include <rte_lpm6.h> +#include <rte_acl.h> +#include <rte_version.h> +#include <rte_hash_crc.h> + +#include "prox_malloc.h" +#include "etypes.h" +#include "prox_lua.h" +#include "log.h" +#include "quit.h" +#include "defines.h" +#include "prox_globals.h" +#include "prox_lua_types.h" +#include "ip_subnet.h" +#include "hash_entry_types.h" +#include "handle_qinq_encap4.h" +#include "toeplitz.h" +#include "handle_lb_5tuple.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +static char error_str[1024]; +static char *cur_pos; + +const char *get_lua_to_errors(void) +{ + return error_str; +} + +static void null_terminate_error(void) +{ + size_t diff = cur_pos - error_str; + + if (diff >= sizeof(error_str) && + error_str[sizeof(error_str) - 1] != 0) + error_str[sizeof(error_str) - 1] = 0; +} + +__attribute__((format(printf, 1, 2))) static void set_err(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + cur_pos = error_str; + cur_pos += vsnprintf(cur_pos, sizeof(error_str) - (cur_pos - error_str), fmt, ap); + null_terminate_error(); + + va_end(ap); +} + +__attribute__((format(printf, 1, 2))) static void concat_err(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + cur_pos += vsnprintf(cur_pos, sizeof(error_str) - (cur_pos - error_str), fmt, ap); + null_terminate_error(); + + va_end(ap); +} + +/* Make sure that an element is on the top of the stack (zero on success) */ +int lua_getfrom(struct lua_State *L, enum lua_place from, const char *name) +{ + switch (from) { + case STACK: + return lua_gettop(L) > 0? 0 : -1; + case TABLE: + if (!lua_istable(L, -1)) { + set_err("Failed to get field '%s' from table (no table)\n", name); + return -1; + } + + lua_pushstring(L, name); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) { + set_err("Field '%s' is missing from table\n", name); + lua_pop(L, 1); + return -1; + } + return 1; + case GLOBAL: + lua_getglobal(L, name); + if (lua_isnil(L, -1)) { + set_err("Couldn't find global data '%s'\n", name); + lua_pop(L, 1); + return -1; + } + return 1; + } + return -1; +} + +int lua_to_ip(struct lua_State *L, enum lua_place from, const char *name, uint32_t *ip) +{ + uint32_t n_entries; + uint32_t ip_array[4]; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 4) { + set_err("Invalid IPv4 format\n"); + return -1; + } + + *ip = 0; + for (int i = 0; i < 4; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + if (!(v >= 0 && v <= 255)) { + set_err("Invalid IPv4 format\n"); + return -1; + } + *ip |= v << (24 - i*8); + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_ip6(struct lua_State *L, enum lua_place from, const char *name, uint8_t *ip) +{ + uint32_t n_entries; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 16) { + set_err("Invalid IPv6 format\n"); + return -1; + } + + for (int i = 0; i < 16; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + ip[i] = v; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_mac(struct lua_State *L, enum lua_place from, const char *name, struct ether_addr *mac) +{ + uint32_t n_entries; + uint32_t mac_array[4]; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 6) { + set_err("Invalid MAC format\n"); + return -1; + } + + for (int i = 0; i < 6; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + if (!(v >= 0 && v <= 255)) { + set_err("Invalid MAC format\n"); + return -1; + } + mac->addr_bytes[i] = v; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_cidr(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr) +{ + uint32_t depth, ip; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("CIDR is not a table\n"); + return -1; + } + + if (lua_to_ip(L, TABLE, "ip", &ip) || + lua_to_int(L, TABLE, "depth", &depth)) { + return -1; + } + cidr->ip = ip; + cidr->prefix = depth; + + lua_pop(L, pop); + return 0; +} + +int lua_to_cidr6(struct lua_State *L, enum lua_place from, const char *name, struct ip6_subnet *cidr) +{ + uint32_t depth; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("CIDR6 is not a table\n"); + return -1; + } + + if (lua_to_ip6(L, TABLE, "ip6", cidr->ip) || + lua_to_int(L, TABLE, "depth", &depth)) { + return -1; + } + cidr->prefix = depth; + + lua_pop(L, pop); + return 0; +} + +int lua_to_val_mask(struct lua_State *L, enum lua_place from, const char *name, struct val_mask *val_mask) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + if (lua_to_int(L, TABLE, "val", &val_mask->val) || + lua_to_int(L, TABLE, "mask", &val_mask->mask)) + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_val_range(struct lua_State *L, enum lua_place from, const char *name, struct val_range *val_range) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + if (lua_to_int(L, TABLE, "beg", &val_range->beg) || + lua_to_int(L, TABLE, "end", &val_range->end)) + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_action(struct lua_State *L, enum lua_place from, const char *name, enum acl_action *action) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isstring(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + const char *s = lua_tostring(L, -1); + + if (!strcmp(s, "drop")) + *action = ACL_DROP; + else if (!strcmp(s, "allow")) + *action = ACL_ALLOW; + else if (!strcmp(s, "rate_limit")) + *action = ACL_RATE_LIMIT; + else + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_string(struct lua_State *L, enum lua_place from, const char *name, char *dst, size_t size) +{ + const char *str; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isstring(L, -1)) { + plog_err("data is not an integer\n"); + return -1; + } + str = lua_tostring(L, -1); + + strncpy(dst, str, size); + + lua_pop(L, pop); + return 0; +} + +int lua_to_port(struct lua_State *L, enum lua_place from, const char *name, uint16_t *port) +{ + double tmp = 0; + int ret; + + ret = lua_to_double(L, from, name, &tmp); + if (ret == 0) + *port = tmp; + return ret; +} + +int lua_to_int(struct lua_State *L, enum lua_place from, const char *name, uint32_t *val) +{ + double tmp = 0; + int ret; + + ret = lua_to_double(L, from, name, &tmp); + if (ret == 0) + *val = tmp; + return ret; +} + +int lua_to_double(struct lua_State *L, enum lua_place from, const char *name, double *val) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isnumber(L, -1)) { + set_err("data is not a number\n"); + return -1; + } + *val = lua_tonumber(L, -1); + + lua_pop(L, pop); + return 0; +} + +int lua_to_routes4_entry(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr, uint32_t *nh_idx) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read routes4 entry since data is not a table\n"); + return -1; + } + + if (lua_to_cidr(L, TABLE, "cidr", cidr) || + lua_to_int(L, TABLE, "next_hop_id", nh_idx)) { + return -1; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_next_hop(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop **nh) +{ + struct next_hop *ret; + uint32_t next_hop_index; + uint32_t port_id; + uint32_t ip; + uint32_t mpls; + struct ether_addr mac; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read next hop since data is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(*ret) * MAX_HOP_INDEX, socket); + PROX_PANIC(ret == NULL, "Could not allocate memory for next hop\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "id", &next_hop_index) || + lua_to_int(L, TABLE, "port_id", &port_id) || + lua_to_ip(L, TABLE, "ip", &ip) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "mpls", &mpls)) + return -1; + + PROX_PANIC(port_id >= PROX_MAX_PORTS, "Port id too high (only supporting %d ports)\n", PROX_MAX_PORTS); + PROX_PANIC(next_hop_index >= MAX_HOP_INDEX, "Next-hop to high (only supporting %d next hops)\n", MAX_HOP_INDEX); + + ret[next_hop_index].mac_port.out_idx = port_id; + ret[next_hop_index].ip_dst = ip; + + ret[next_hop_index].mac_port.mac = mac; + ret[next_hop_index].mpls = mpls; + + lua_pop(L, 1); + } + + *nh = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_next_hop6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop6 **nh) +{ + struct next_hop6 *ret; + uint32_t next_hop_index, port_id, mpls; + struct ether_addr mac; + uint8_t ip[16]; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(*ret) * MAX_HOP_INDEX, socket); + PROX_PANIC(ret == NULL, "Could not allocate memory for next hop\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "id", &next_hop_index) || + lua_to_int(L, TABLE, "port_id", &port_id) || + lua_to_ip6(L, TABLE, "ip6", ip) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "mpls", &mpls)) + return -1; + + PROX_PANIC(port_id >= PROX_MAX_PORTS, "Port id too high (only supporting %d ports)\n", PROX_MAX_PORTS); + PROX_PANIC(next_hop_index >= MAX_HOP_INDEX, "Next-hop to high (only supporting %d next hops)\n", MAX_HOP_INDEX); + + ret[next_hop_index].mac_port.out_idx = port_id; + memcpy(ret[next_hop_index].ip_dst,ip, 16); + + ret[next_hop_index].mac_port.mac = mac; + ret[next_hop_index].mpls = mpls; + + lua_pop(L, 1); + } + + *nh = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_routes4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 *lpm) +{ + struct ip4_subnet dst; + uint32_t next_hop_index; + uint32_t n_loaded_rules; + uint32_t n_tot_rules; + struct rte_lpm *new_lpm; + char lpm_name[64]; + int ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + snprintf(lpm_name, sizeof(lpm_name), "IPv4_lpm_s%u", socket); + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_tot_rules = lua_tointeger(L, -1); + n_loaded_rules = 0; + lua_pop(L, 1); +#if RTE_VERSION >= RTE_VERSION_NUM(16,4,0,1) + struct rte_lpm_config conf; + conf.max_rules = 2 * n_tot_rules; + conf.number_tbl8s = 256; + conf.flags = 0; + new_lpm = rte_lpm_create(lpm_name, socket, &conf); +#else + new_lpm = rte_lpm_create(lpm_name, socket, 2 * n_tot_rules, 0); +#endif + PROX_PANIC(NULL == new_lpm, "Failed to allocate lpm\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_routes4_entry(L, STACK, NULL, &dst, &next_hop_index)) { + set_err("Failed to read entry while setting up lpm\n"); + return -1; + } + ret = rte_lpm_add(new_lpm, dst.ip, dst.prefix, next_hop_index); + + if (ret != 0) { + set_err("Failed to add (%d) index %u ip %x/%u to lpm\n", + ret, next_hop_index, dst.ip, dst.prefix); + } + else if (++n_loaded_rules % 10000 == 0) { + plog_info("Route %d added\n", n_loaded_rules); + } + + lua_pop(L, 1); + } + + lpm->rte_lpm = new_lpm; + lpm->n_used_rules = n_loaded_rules; + lpm->n_free_rules = 2 * n_tot_rules - n_loaded_rules; + + lua_pop(L, pop); + return 0; +} + +int lua_to_lpm4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 **lpm) +{ + struct lpm4 *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + ret = prox_zmalloc(sizeof(struct lpm4), socket); + + if (!lua_istable(L, -1)) { + set_err("Can't read lpm4 since data is not a table\n"); + return -1; + } + + if (lua_to_routes4(L, TABLE, "routes", socket, ret) || + lua_to_next_hop(L, TABLE, "next_hops", socket, &ret->next_hops)) { + return -1; + } + + if (ret->rte_lpm) + plog_info("Loaded %d routes\n", ret->n_used_rules); + + *lpm = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_lpm6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 **lpm) +{ + struct lpm6 *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Lpm6 is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(struct lpm6), socket); + + if (lua_to_routes6(L, TABLE, "routes6", socket, ret) || + lua_to_next_hop6(L, TABLE, "next_hops6", socket, &ret->next_hops)) + return -1; + + if (ret->rte_lpm6) + plog_info("Loaded %d routes\n", ret->n_used_rules); + + *lpm = ret; + + lua_pop(L, pop); + return 0; +} + +static int lua_to_lpm6_entry(struct lua_State *L, enum lua_place from, const char *name, struct ip6_subnet *cidr, uint32_t *nh_idx) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("lpm6 entry is not a table\n"); + return -1; + } + if (lua_to_cidr6(L, TABLE, "cidr6", cidr) || + lua_to_int(L, TABLE, "next_hop_id", nh_idx)) { + return -1; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_routes6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 *lpm) +{ + struct ip6_subnet dst; + uint32_t next_hop_index; + uint32_t n_loaded_rules; + struct rte_lpm6 *new_lpm; + struct rte_lpm6_config config; + uint32_t n_tot_rules; + char lpm_name[64]; + int ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + snprintf(lpm_name, sizeof(lpm_name), "IPv6_lpm_s%u", socket); + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_tot_rules = lua_tointeger(L, -1); + n_loaded_rules = 0; + lua_pop(L, 1); + + config.max_rules = n_tot_rules; + config.number_tbl8s = (1 << 16); + config.flags = 0; + + new_lpm = rte_lpm6_create(lpm_name, socket, &config); + PROX_PANIC(NULL == new_lpm, "Failed to allocate lpm\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + + if (lua_to_lpm6_entry(L, STACK, NULL, &dst, &next_hop_index)) { + concat_err("Failed to read entry while setting up lpm\n"); + return -1; + } + + ret = rte_lpm6_add(new_lpm, dst.ip, dst.prefix, next_hop_index); + + if (ret != 0) { + plog_warn("Failed to add (%d) index %u, %d\n", + ret, next_hop_index, dst.prefix); + } + else if (++n_loaded_rules % 10000 == 0) { + plog_info("Route %d added\n", n_loaded_rules); + } + + lua_pop(L, 1); + } + + lpm->rte_lpm6 = new_lpm; + lpm->n_used_rules = n_loaded_rules; + lpm->n_free_rules = 2 * n_tot_rules - n_loaded_rules; + + lua_pop(L, pop); + return 0; +} + +int lua_to_dscp(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint8_t **dscp) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("DSCP is not a table\n"); + return -1; + } + + uint32_t dscp_bits, tc, queue; + int status; + *dscp = prox_zmalloc(64, socket); + PROX_PANIC(dscp == NULL, "Error creating dscp table"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "dscp", &dscp_bits) || + lua_to_int(L, TABLE, "tc", &tc) || + lua_to_int(L, TABLE, "queue", &queue)) { + concat_err("Failed to read dscp config\n"); + return -1; + } + + lua_pop(L, 1); + + (*dscp)[dscp_bits] = tc << 2 | queue; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_qinq_gre_map(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct qinq_gre_map **qinq_gre_map) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + if (from != STACK) + set_err("QinQ to gre map is not a table\n"); + else + set_err("QinQ to gre map %s is not a table\n", name); + return -1; + } + + struct qinq_gre_map *ret; + uint32_t svlan, cvlan; + uint16_t be_svlan, be_cvlan; + uint32_t user; + uint32_t gre_id; + + uint32_t n_entries; + uint32_t idx = 0; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + size_t mem_size = 0; + mem_size += sizeof(struct qinq_gre_map); + mem_size += n_entries * sizeof(struct qinq_gre_entry); + + ret = prox_zmalloc(mem_size, socket); + PROX_PANIC(ret == NULL, "Error creating gre_qinq map"); + + ret->count = n_entries; + + lua_pushnil(L); + while (lua_next(L, -2)) { + + if (lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_int(L, TABLE, "gre_id", &gre_id) || + lua_to_int(L, TABLE, "user_id", &user)) { + concat_err("Failed to read user table config\n"); + return -1; + } + + be_svlan = rte_bswap16((uint16_t)svlan); + be_cvlan = rte_bswap16((uint16_t)cvlan); + + ret->entries[idx].user = user; + ret->entries[idx].svlan = be_svlan; + ret->entries[idx].cvlan = be_cvlan; + ret->entries[idx].gre_id = gre_id; + ret->entries[idx].rss = toeplitz_hash((uint8_t *)&be_cvlan, 4); + + plog_dbg("elem %u: be_svlan=%x, be_cvlan=%x, rss_input=%x, rss=%x, gre_id=%x\n", + idx, be_svlan, be_cvlan, be_cvlan, ret->entries[idx].rss, gre_id); + + idx++; + lua_pop(L, 1); + } + + *qinq_gre_map = ret; + + lua_pop(L, pop); + return 0; +} + +int lua_to_user_table(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint16_t **user_table) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + uint32_t svlan, cvlan; + uint16_t be_svlan, be_cvlan; + uint32_t user; + + *user_table = prox_zmalloc(0x1000000 * sizeof(uint16_t), socket); + PROX_PANIC(*user_table == NULL, "Error creating user table"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_int(L, TABLE, "user_id", &user)) { + concat_err("Failed to read user table config\n"); + return -1; + } + + be_svlan = rte_bswap16((uint16_t)svlan); + be_cvlan = rte_bswap16((uint16_t)cvlan); + + (*user_table)[PKT_TO_LUTQINQ(be_svlan, be_cvlan)] = user; + + lua_pop(L, 1); + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_ip6_tun_binding(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct ipv6_tun_binding_table **data) +{ + struct ipv6_tun_binding_table *ret; + uint32_t n_entries; + uint32_t idx = 0; + uint32_t port = 0; + size_t memsize = 0; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read IPv6 tunnel bindings entry since ret is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + memsize = sizeof(struct ipv6_tun_binding_table); + memsize += n_entries * sizeof(struct ipv6_tun_binding_entry); + + ret = prox_zmalloc(memsize, socket); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_ip6(L, TABLE, "ip6", ret->entry[idx].endpoint_addr.bytes) || + lua_to_mac(L, TABLE, "mac", &ret->entry[idx].next_hop_mac) || + lua_to_ip(L, TABLE, "ip", &ret->entry[idx].public_ipv4) || + lua_to_int(L, TABLE, "port", &port)) + return -1; + + ret->entry[idx].public_port = port; + idx++; + lua_pop(L, 1); + } + ret->num_binding_entries = idx; + + plog_info("\tRead %d IPv6 Tunnel Binding entries\n", idx); + + *data = ret; + + lua_pop(L, pop); + return 0; +} + +int lua_to_cpe_table_data(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct cpe_table_data **data) +{ + struct cpe_table_data *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read IPv6 tunnel bindings entry since ret is not a table\n"); + return -1; + } + + /* Each entry in the input table expands to multiple entries + depending on the number of hosts within the subnet. For + this reason, go through the whole table and find out how + many entries will be added in total. */ + struct ip4_subnet cidr; + uint32_t n_entries = 0; + uint32_t port_idx, gre_id, svlan, cvlan, user; + struct ether_addr mac; + uint32_t idx = 0; + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_cidr(L, TABLE, "cidr", &cidr)) + return -1; + n_entries += ip4_subet_get_n_hosts(&cidr); + lua_pop(L, 1); + } + + ret = prox_zmalloc(sizeof(*ret) + n_entries * sizeof(struct cpe_table_entry), 0); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "dest_id", &port_idx) || + lua_to_int(L, TABLE, "gre_id", &gre_id) || + lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_cidr(L, TABLE, "cidr", &cidr) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "user_id", &user)) + return -1; + + uint32_t n_hosts = ip4_subet_get_n_hosts(&cidr); + + for (uint32_t i = 0; i < n_hosts; ++i) { + ret->entries[idx].port_idx = port_idx; + ret->entries[idx].gre_id = gre_id; + ret->entries[idx].svlan = rte_bswap16(svlan); + ret->entries[idx].cvlan = rte_bswap16(cvlan); + ret->entries[idx].eth_addr = mac; + ret->entries[idx].user = user; + + PROX_PANIC(ip4_subnet_to_host(&cidr, i, &ret->entries[idx].ip), "Invalid host in address\n"); + ret->entries[idx].ip = rte_bswap32(ret->entries[idx].ip); + idx++; + } + + lua_pop(L, 1); + } + + ret->n_entries = n_entries; + *data = ret; + + lua_pop(L, pop); + return 0; +} + +struct acl4_rule { + struct rte_acl_rule_data data; + struct rte_acl_field fields[9]; +}; + +int lua_to_rules(struct lua_State *L, enum lua_place from, const char *name, struct rte_acl_ctx *ctx, uint32_t* n_max_rules, int use_qinq, uint16_t qinq_tag) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read rules since data is not a table\n"); + return -1; + } + + struct val_mask svlan, cvlan, ip_proto; + struct ip4_subnet src_cidr, dst_cidr; + struct val_range sport, dport; + enum acl_action action; + uint32_t n_rules = 0; + lua_pushnil(L); + while (lua_next(L, -2)) { + if (n_rules == *n_max_rules) { + set_err("Too many rules"); + return -1; + } + if (use_qinq) { + if (lua_to_val_mask(L, TABLE, "svlan_id", &svlan) || + lua_to_val_mask(L, TABLE, "cvlan_id", &cvlan)) + return -1; + } + + if (lua_to_val_mask(L, TABLE, "ip_proto", &ip_proto) || + lua_to_cidr(L, TABLE, "src_cidr", &src_cidr) || + lua_to_cidr(L, TABLE, "dst_cidr", &dst_cidr) || + lua_to_val_range(L, TABLE, "sport", &sport) || + lua_to_val_range(L, TABLE, "dport", &dport) || + lua_to_action(L, TABLE, "action", &action)) + return -1; + + struct acl4_rule rule; + + rule.data.userdata = action; /* allow, drop or rate_limit */ + rule.data.category_mask = 1; + rule.data.priority = n_rules++; + + /* Configuration for rules is done in little-endian so no bswap is needed here.. */ + + rule.fields[0].value.u8 = ip_proto.val; + rule.fields[0].mask_range.u8 = ip_proto.mask; + rule.fields[1].value.u32 = src_cidr.ip; + rule.fields[1].mask_range.u32 = src_cidr.prefix; + + rule.fields[2].value.u32 = dst_cidr.ip; + rule.fields[2].mask_range.u32 = dst_cidr.prefix; + + rule.fields[3].value.u16 = sport.beg; + rule.fields[3].mask_range.u16 = sport.end; + + rule.fields[4].value.u16 = dport.beg; + rule.fields[4].mask_range.u16 = dport.end; + + if (use_qinq) { + rule.fields[5].value.u16 = rte_bswap16(qinq_tag); + rule.fields[5].mask_range.u16 = 0xffff; + + /* To mask out the TCI and only keep the VID, the mask should be 0x0fff */ + rule.fields[6].value.u16 = svlan.val; + rule.fields[6].mask_range.u16 = svlan.mask; + + rule.fields[7].value.u16 = rte_bswap16(ETYPE_VLAN); + rule.fields[7].mask_range.u16 = 0xffff; + + rule.fields[8].value.u16 = cvlan.val; + rule.fields[8].mask_range.u16 = cvlan.mask; + } + else { + /* Reuse first ethertype from vlan to check if packet is IPv4 packet */ + rule.fields[5].value.u16 = rte_bswap16(ETYPE_IPv4); + rule.fields[5].mask_range.u16 = 0xffff; + + /* Other fields are ignored */ + rule.fields[6].value.u16 = 0; + rule.fields[6].mask_range.u16 = 0; + rule.fields[7].value.u16 = 0; + rule.fields[7].mask_range.u16 = 0; + rule.fields[8].value.u16 = 0; + rule.fields[8].mask_range.u16 = 0; + } + + rte_acl_add_rules(ctx, (struct rte_acl_rule*) &rule, 1); + lua_pop(L, 1); + } + + *n_max_rules -= n_rules; + lua_pop(L, pop); + return 0; +} + +static inline uint32_t ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) +{ + const union ipv4_5tuple_host *k; + uint32_t t; + const uint32_t *p; + + k = data; + t = k->proto; + p = (const uint32_t *)&k->port_src; + + init_val = rte_hash_crc_4byte(t, init_val); + init_val = rte_hash_crc_4byte(k->ip_src, init_val); + init_val = rte_hash_crc_4byte(k->ip_dst, init_val); + init_val = rte_hash_crc_4byte(*p, init_val); + return (init_val); +} + +int lua_to_tuples(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct rte_hash **lookup_hash, uint8_t **out_if) +{ + int pop; + char s[64]; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + plog_err("Can't read rules since data is not a table\n"); + return -1; + } + + lua_len(L, -1); + uint32_t n_tot_tuples = lua_tointeger(L, -1); + lua_pop(L, 1); + + struct rte_hash_parameters ipv4_l3fwd_hash_params = { + .name = NULL, + .entries = n_tot_tuples * 4, + .key_len = sizeof(union ipv4_5tuple_host), +#if RTE_VERSION < RTE_VERSION_NUM(2, 1, 0, 0) + .bucket_entries = 4, +#endif + .hash_func = ipv4_hash_crc, + .hash_func_init_val = 0, + }; + + /* create lb_5tuple hash - same hash is shared between cores on same socket */ + snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socket); + if ((*lookup_hash = rte_hash_find_existing(s)) == NULL) { + ipv4_l3fwd_hash_params.name = s; + ipv4_l3fwd_hash_params.socket_id = socket; + *lookup_hash = rte_hash_create(&ipv4_l3fwd_hash_params); + PROX_PANIC(*lookup_hash == NULL, "Unable to create the lb_5tuple hash\n"); + } + + lua_pushnil(L); + while (lua_next(L, -2)) { + uint32_t if_out, ip_src, ip_dst, port_src, port_dst, proto; + union ipv4_5tuple_host newkey; + + if (lua_to_int(L, TABLE, "if_out", &if_out) || + lua_to_int(L, TABLE, "ip_src", &ip_src) || + lua_to_int(L, TABLE, "ip_dst", &ip_dst) || + lua_to_int(L, TABLE, "port_src", &port_src) || + lua_to_int(L, TABLE, "port_dst", &port_dst) || + lua_to_int(L, TABLE, "proto", &proto)) { + plog_err("Failed to read user table config\n"); + return -1; + } + + newkey.ip_dst = rte_cpu_to_be_32(ip_dst); + newkey.ip_src = rte_cpu_to_be_32(ip_src); + newkey.port_dst = rte_cpu_to_be_16((uint16_t)port_dst); + newkey.port_src = rte_cpu_to_be_16((uint16_t)port_src); + newkey.proto = (uint8_t)proto; + newkey.pad0 = 0; + newkey.pad1 = 0; + + int32_t ret = rte_hash_add_key(*lookup_hash, (void *) &newkey); + PROX_PANIC(ret < 0, "Unable to add entry (err code %d)\n", ret); + (*out_if)[ret] = (uint8_t) if_out; + + lua_pop(L, 1); + } + lua_pop(L, pop); + return 0; +} diff --git a/VNFs/DPPD-PROX/prox_lua_types.h b/VNFs/DPPD-PROX/prox_lua_types.h new file mode 100644 index 00000000..182c9055 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_lua_types.h @@ -0,0 +1,142 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_LUA_TYPES_H_ +#define _PROX_LUA_TYPES_H_ + +#include <inttypes.h> +#include <rte_ether.h> +#include <rte_hash.h> + +#include "ip6_addr.h" + +struct lua_State; +struct ether_addr; +struct ip4_subnet; +struct ip6_subnet; +struct next_hop; +struct rte_lpm; +struct rte_lpm6; +struct next_hop6; +struct rte_acl_ctx; +struct qinq_gre_map; + +#define MAX_HOP_INDEX 128 +enum l4gen_peer {PEER_SERVER, PEER_CLIENT}; + +static const char *l4gen_peer_to_str(enum l4gen_peer peer) +{ + return peer == PEER_SERVER? "server" : "client"; +} + +struct peer_data { + uint8_t *hdr; + uint32_t hdr_len; + uint8_t *content; +}; + +struct peer_action { + enum l4gen_peer peer; + uint32_t beg; + uint32_t len; +}; + +struct lpm4 { + uint32_t n_free_rules; + uint32_t n_used_rules; + struct next_hop *next_hops; + struct rte_lpm *rte_lpm; +}; + +struct lpm6 { + struct rte_lpm6 *rte_lpm6; + struct next_hop6 *next_hops; + uint32_t n_free_rules; + uint32_t n_used_rules; +}; + +struct ipv6_tun_binding_entry { + struct ipv6_addr endpoint_addr; // IPv6 local addr + struct ether_addr next_hop_mac; // mac addr of next hop towards lwB4 + uint32_t public_ipv4; // Public IPv4 address + uint16_t public_port; // Public base port (together with port mask, defines the Port Set) +} __attribute__((__packed__)); + +struct ipv6_tun_binding_table { + uint32_t num_binding_entries; + struct ipv6_tun_binding_entry entry[0]; +}; + +struct cpe_table_entry { + uint32_t port_idx; + uint32_t gre_id; + uint32_t svlan; + uint32_t cvlan; + uint32_t ip; + struct ether_addr eth_addr; + uint32_t user; +}; + +struct cpe_table_data { + uint32_t n_entries; + struct cpe_table_entry entries[0]; +}; + +struct val_mask { + uint32_t val; + uint32_t mask; +}; + +struct val_range { + uint32_t beg; + uint32_t end; +}; + +enum acl_action {ACL_NOT_SET, ACL_ALLOW, ACL_DROP, ACL_RATE_LIMIT}; + +const char *get_lua_to_errors(void); + +enum lua_place {STACK, TABLE, GLOBAL}; +int lua_getfrom(struct lua_State *L, enum lua_place from, const char *name); + +int lua_to_port(struct lua_State *L, enum lua_place from, const char *name, uint16_t *port); +int lua_to_ip(struct lua_State *L, enum lua_place from, const char *name, uint32_t *ip); +int lua_to_ip6(struct lua_State *L, enum lua_place from, const char *name, uint8_t *ip); +int lua_to_mac(struct lua_State *L, enum lua_place from, const char *name, struct ether_addr *mac); +int lua_to_cidr(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr); +int lua_to_cidr6(struct lua_State *L, enum lua_place from, const char *name, struct ip6_subnet *cidr); +int lua_to_int(struct lua_State *L, enum lua_place from, const char *name, uint32_t *val); +int lua_to_double(struct lua_State *L, enum lua_place from, const char *name, double *val); +int lua_to_string(struct lua_State *L, enum lua_place from, const char *name, char *dst, size_t size); +int lua_to_val_mask(struct lua_State *L, enum lua_place from, const char *name, struct val_mask *val_mask); +int lua_to_val_range(struct lua_State *L, enum lua_place from, const char *name, struct val_range *val_range); +int lua_to_action(struct lua_State *L, enum lua_place from, const char *name, enum acl_action *action); +int lua_to_dscp(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint8_t **dscp); +int lua_to_user_table(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint16_t **user_table); +int lua_to_lpm4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 **lpm); +int lua_to_routes4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 *lpm); +int lua_to_next_hop(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop **nh); +int lua_to_lpm6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 **lpm); +int lua_to_ip6_tun_binding(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct ipv6_tun_binding_table **data); +int lua_to_qinq_gre_map(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct qinq_gre_map **qinq_gre_map); +int lua_to_cpe_table_data(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct cpe_table_data **data); +int lua_to_rules(struct lua_State *L, enum lua_place from, const char *name, struct rte_acl_ctx *ctx, uint32_t* n_max_rules, int use_qinq, uint16_t qinq_tag); +int lua_to_routes4_entry(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr, uint32_t *nh_idx); +int lua_to_next_hop6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop6 **nh); +int lua_to_routes6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 *lpm); +int lua_to_tuples(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct rte_hash **lookup_hash, uint8_t **out_if); + +#endif /* _PROX_LUA_TYPES_H_ */ diff --git a/VNFs/DPPD-PROX/prox_malloc.c b/VNFs/DPPD-PROX/prox_malloc.c new file mode 100644 index 00000000..cec80f3e --- /dev/null +++ b/VNFs/DPPD-PROX/prox_malloc.c @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_malloc.h> + +#include "prox_malloc.h" + +#ifndef RTE_CACHE_LINE_SIZE +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +void *prox_zmalloc(size_t size, int socket) +{ + return rte_zmalloc_socket(NULL, size, RTE_CACHE_LINE_SIZE, socket); +} + +void prox_free(void *ptr) +{ + rte_free(ptr); +} diff --git a/VNFs/DPPD-PROX/prox_malloc.h b/VNFs/DPPD-PROX/prox_malloc.h new file mode 100644 index 00000000..c75667de --- /dev/null +++ b/VNFs/DPPD-PROX/prox_malloc.h @@ -0,0 +1,25 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_MALLOC_H_ +#define _PROX_MALLOC_H_ + +#include <stddef.h> + +void *prox_zmalloc(size_t size, int socket); +void prox_free(void *ptr); + +#endif /* _PROX_MALLOC_H_ */ diff --git a/VNFs/DPPD-PROX/prox_port_cfg.c b/VNFs/DPPD-PROX/prox_port_cfg.c new file mode 100644 index 00000000..831a8ff9 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_port_cfg.c @@ -0,0 +1,473 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> +#include <rte_version.h> +#include <rte_eth_ring.h> +#include <rte_mbuf.h> +#if (RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0)) && (RTE_VERSION <= RTE_VERSION_NUM(17,5,0,1)) +#include <rte_eth_null.h> +#endif + +#include "prox_port_cfg.h" +#include "prox_globals.h" +#include "log.h" +#include "quit.h" +#include "defaults.h" +#include "toeplitz.h" +#include "defines.h" +#include "prox_cksum.h" + +struct prox_port_cfg prox_port_cfg[PROX_MAX_PORTS]; +rte_atomic32_t lsc; + +int prox_nb_active_ports(void) +{ + int ret = 0; + for (uint32_t i = 0; i < PROX_MAX_PORTS; ++i) { + ret += prox_port_cfg[i].active; + } + return ret; +} + +int prox_last_port_active(void) +{ + int ret = 0; + for (uint32_t i = 0; i < PROX_MAX_PORTS; ++i) { + if (prox_port_cfg[i].active) { + ret = i; + } + } + return ret; +} + +static void lsc_cb(__attribute__((unused)) uint8_t port_id, enum rte_eth_event_type type, __attribute__((unused)) void *param) +{ + struct rte_eth_link link; + + if (RTE_ETH_EVENT_INTR_LSC != type) { + return; + } + + rte_atomic32_inc(&lsc); +} + +struct prox_pktmbuf_reinit_args { + struct rte_mempool *mp; + struct lcore_cfg *lconf; +}; + +/* standard mbuf initialization procedure */ +void prox_pktmbuf_init(struct rte_mempool *mp, void *opaque_arg, void *_m, unsigned i) +{ + struct rte_mbuf *mbuf = _m; + +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + mbuf->tx_offload = CALC_TX_OL(sizeof(struct ether_hdr), sizeof(struct ipv4_hdr)); +#else + mbuf->pkt.vlan_macip.f.l2_len = sizeof(struct ether_hdr); + mbuf->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr); +#endif + + rte_pktmbuf_init(mp, opaque_arg, mbuf, i); +} + +void prox_pktmbuf_reinit(void *arg, void *start, __attribute__((unused)) void *end, uint32_t idx) +{ + struct prox_pktmbuf_reinit_args *init_args = arg; + struct rte_mbuf *m; + char* obj = start; + + obj += init_args->mp->header_size; + m = (struct rte_mbuf*)obj; + + prox_pktmbuf_init(init_args->mp, init_args->lconf, obj, idx); +} + +/* initialize rte devices and check the number of available ports */ +void init_rte_dev(int use_dummy_devices) +{ + uint8_t nb_ports, port_id_max, port_id_last; + struct rte_eth_dev_info dev_info; + + nb_ports = rte_eth_dev_count(); + /* get available ports configuration */ + PROX_PANIC(use_dummy_devices && nb_ports, "Can't use dummy devices while there are also real ports\n"); + + if (use_dummy_devices) { +#if (RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0)) && (RTE_VERSION <= RTE_VERSION_NUM(17,5,0,1)) + nb_ports = prox_last_port_active() + 1; + plog_info("Creating %u dummy devices\n", nb_ports); + + char port_name[32] = "0dummy_dev"; + for (uint32_t i = 0; i < nb_ports; ++i) { + eth_dev_null_create(port_name, 0, ETHER_MIN_LEN, 0); + port_name[0]++; + } +#else + PROX_PANIC(use_dummy_devices, "Can't use dummy devices\n"); +#endif + } + else { + PROX_PANIC(nb_ports == 0, "\tError: DPDK could not find any port\n"); + plog_info("\tDPDK has found %u ports\n", nb_ports); + } + + if (nb_ports > PROX_MAX_PORTS) { + plog_warn("\tWarning: I can deal with at most %u ports." + " Please update PROX_MAX_PORTS and recompile.\n", PROX_MAX_PORTS); + + nb_ports = PROX_MAX_PORTS; + } + port_id_max = nb_ports - 1; + port_id_last = prox_last_port_active(); + PROX_PANIC(port_id_last > port_id_max, + "\tError: invalid port(s) specified, last port index active: %d (max index is %d)\n", + port_id_last, port_id_max); + + /* Assign ports to PROX interfaces & Read max RX/TX queues per port */ + for (uint8_t port_id = 0; port_id < nb_ports; ++port_id) { + /* skip ports that are not enabled */ + if (!prox_port_cfg[port_id].active) { + continue; + } + plog_info("\tGetting info for rte dev %u\n", port_id); + rte_eth_dev_info_get(port_id, &dev_info); + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + port_cfg->socket = -1; + + port_cfg->max_txq = dev_info.max_tx_queues; + port_cfg->max_rxq = dev_info.max_rx_queues; + + if (!dev_info.pci_dev) + continue; + + snprintf(port_cfg->pci_addr, sizeof(port_cfg->pci_addr), + "%04x:%02x:%02x.%1x", dev_info.pci_dev->addr.domain, dev_info.pci_dev->addr.bus, dev_info.pci_dev->addr.devid, dev_info.pci_dev->addr.function); + strncpy(port_cfg->driver_name, dev_info.driver_name, sizeof(port_cfg->driver_name)); + plog_info("\tPort %u : driver='%s' tx_queues=%d rx_queues=%d\n", port_id, !strcmp(port_cfg->driver_name, "")? "null" : port_cfg->driver_name, port_cfg->max_txq, port_cfg->max_rxq); + + if (strncmp(port_cfg->driver_name, "rte_", 4) == 0) { + strncpy(port_cfg->short_name, prox_port_cfg[port_id].driver_name + 4, sizeof(port_cfg->short_name)); + } else if (strncmp(port_cfg->driver_name, "net_", 4) == 0) { + strncpy(port_cfg->short_name, prox_port_cfg[port_id].driver_name + 4, sizeof(port_cfg->short_name)); + } else { + strncpy(port_cfg->short_name, prox_port_cfg[port_id].driver_name, sizeof(port_cfg->short_name)); + } + char *ptr; + if ((ptr = strstr(port_cfg->short_name, "_pmd")) != NULL) { + *ptr = '\x0'; + } + + /* Try to find the device's numa node */ + char buf[1024]; + snprintf(buf, sizeof(buf), "/sys/bus/pci/devices/%s/numa_node", port_cfg->pci_addr); + FILE* numa_node_fd = fopen(buf, "r"); + if (numa_node_fd) { + if (fgets(buf, sizeof(buf), numa_node_fd) == NULL) { + plog_warn("Failed to read numa_node for device %s\n", port_cfg->pci_addr); + } + port_cfg->socket = strtol(buf, 0, 0); + if (port_cfg->socket == -1) { + plog_warn("System did not report numa_node for device %s\n", port_cfg->pci_addr); + } + fclose(numa_node_fd); + } + + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_IPV4_CKSUM) { + port_cfg->capabilities.tx_offload_cksum |= IPV4_CKSUM; + } + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM) { + port_cfg->capabilities.tx_offload_cksum |= UDP_CKSUM; + } + } +} + +/* Create rte ring-backed devices */ +uint8_t init_rte_ring_dev(void) +{ + uint8_t nb_ring_dev = 0; + + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + /* skip ports that are not enabled */ + if (!prox_port_cfg[port_id].active) { + continue; + } + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + if (port_cfg->rx_ring[0] != '\0') { + plog_info("\tRing-backed port %u: rx='%s' tx='%s'\n", port_id, port_cfg->rx_ring, port_cfg->tx_ring); + + struct rte_ring* rx_ring = rte_ring_lookup(port_cfg->rx_ring); + PROX_PANIC(rx_ring == NULL, "Ring %s not found for port %d!\n", port_cfg->rx_ring, port_id); + struct rte_ring* tx_ring = rte_ring_lookup(port_cfg->tx_ring); + PROX_PANIC(tx_ring == NULL, "Ring %s not found for port %d!\n", port_cfg->tx_ring, port_id); + + int ret = rte_eth_from_rings(port_cfg->name, &rx_ring, 1, &tx_ring, 1, rte_socket_id()); + PROX_PANIC(ret != 0, "Failed to create eth_dev from rings for port %d\n", port_id); + + port_cfg->port_conf.intr_conf.lsc = 0; /* Link state interrupt not supported for ring-backed ports */ + + nb_ring_dev++; + } + } + + return nb_ring_dev; +} + +static void init_port(struct prox_port_cfg *port_cfg) +{ + static char dummy_pool_name[] = "0_dummy"; + struct rte_eth_link link; + uint8_t port_id; + int ret; + + port_id = port_cfg - prox_port_cfg; + plog_info("\t*** Initializing port %u ***\n", port_id); + plog_info("\t\tPort name is set to %s\n", port_cfg->name); + plog_info("\t\tPort max RX/TX queue is %u/%u\n", port_cfg->max_rxq, port_cfg->max_txq); + plog_info("\t\tPort driver is %s\n", port_cfg->driver_name); + + PROX_PANIC(port_cfg->n_rxq == 0 && port_cfg->n_txq == 0, + "\t\t port %u is enabled but no RX or TX queues have been configured", port_id); + + if (port_cfg->n_rxq == 0) { + /* not receiving on this port */ + plog_info("\t\tPort %u had no RX queues, setting to 1\n", port_id); + port_cfg->n_rxq = 1; + uint32_t mbuf_size = MBUF_SIZE; + if (strcmp(port_cfg->short_name, "vmxnet3") == 0) { + mbuf_size = MBUF_SIZE + RTE_PKTMBUF_HEADROOM; + } + plog_info("\t\tAllocating dummy memory pool on socket %u with %u elements of size %u\n", + port_cfg->socket, port_cfg->n_rxd, mbuf_size); + port_cfg->pool[0] = rte_mempool_create(dummy_pool_name, port_cfg->n_rxd, mbuf_size, + 0, + sizeof(struct rte_pktmbuf_pool_private), + rte_pktmbuf_pool_init, NULL, + prox_pktmbuf_init, 0, + port_cfg->socket, 0); + PROX_PANIC(port_cfg->pool[0] == NULL, "Failed to allocate dummy memory pool on socket %u with %u elements\n", + port_cfg->socket, port_cfg->n_rxd); + dummy_pool_name[0]++; + } else { + // Most pmd do not support setting mtu yet... + if (!strcmp(port_cfg->short_name, "ixgbe")) { + plog_info("\t\tSetting MTU size to %u for port %u ...\n", port_cfg->mtu, port_id); + ret = rte_eth_dev_set_mtu(port_id, port_cfg->mtu); + PROX_PANIC(ret < 0, "\n\t\t\trte_eth_dev_set_mtu() failed on port %u: error %d\n", port_id, ret); + } + + if (port_cfg->n_txq == 0) { + /* not sending on this port */ + plog_info("\t\tPort %u had no TX queues, setting to 1\n", port_id); + port_cfg->n_txq = 1; + } + } + + if (port_cfg->n_rxq > 1) { + // Enable RSS if multiple receive queues + port_cfg->port_conf.rxmode.mq_mode |= ETH_MQ_RX_RSS; + port_cfg->port_conf.rx_adv_conf.rss_conf.rss_key = toeplitz_init_key; + port_cfg->port_conf.rx_adv_conf.rss_conf.rss_key_len = TOEPLITZ_KEY_LEN; +#if RTE_VERSION >= RTE_VERSION_NUM(2,0,0,0) + port_cfg->port_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IPV4|ETH_RSS_NONFRAG_IPV4_UDP; +#else + port_cfg->port_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IPV4|ETH_RSS_NONF_IPV4_UDP; +#endif + } + + plog_info("\t\tConfiguring port %u... with %u RX queues and %u TX queues\n", + port_id, port_cfg->n_rxq, port_cfg->n_txq); + + PROX_PANIC(port_cfg->n_rxq > port_cfg->max_rxq, "\t\t\tToo many RX queues (configuring %u, max is %u)\n", port_cfg->n_rxq, port_cfg->max_rxq); + PROX_PANIC(port_cfg->n_txq > port_cfg->max_txq, "\t\t\tToo many TX queues (configuring %u, max is %u)\n", port_cfg->n_txq, port_cfg->max_txq); + + if (!strcmp(port_cfg->short_name, "ixgbe_vf") || + !strcmp(port_cfg->short_name, "virtio") || +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) + !strcmp(port_cfg->short_name, "i40e") || +#endif + !strcmp(port_cfg->short_name, "i40e_vf") || + !strcmp(port_cfg->driver_name, "") || /* NULL device */ + !strcmp(port_cfg->short_name, "vmxnet3")) { + port_cfg->port_conf.intr_conf.lsc = 0; + plog_info("\t\tDisabling link state interrupt for vmxnet3/VF/virtio (unsupported)\n"); + } + + if (port_cfg->lsc_set_explicitely) { + port_cfg->port_conf.intr_conf.lsc = port_cfg->lsc_val; + plog_info("\t\tOverriding link state interrupt configuration to '%s'\n", port_cfg->lsc_val? "enabled" : "disabled"); + } + if (!strcmp(port_cfg->short_name, "vmxnet3")) { + if (port_cfg->n_txd < 512) { + // Vmxnet3 driver requires minimum 512 tx descriptors + plog_info("\t\tNumber of TX descriptors is set to 512 (minimum required for vmxnet3\n"); + port_cfg->n_txd = 512; + } + } + + ret = rte_eth_dev_configure(port_id, port_cfg->n_rxq, + port_cfg->n_txq, &port_cfg->port_conf); + PROX_PANIC(ret < 0, "\t\t\trte_eth_dev_configure() failed on port %u: %s (%d)\n", port_id, strerror(-ret), ret); + + if (port_cfg->port_conf.intr_conf.lsc) { + rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_INTR_LSC, lsc_cb, NULL); + } + + plog_info("\t\tMAC address set to "MAC_BYTES_FMT"\n", MAC_BYTES(port_cfg->eth_addr.addr_bytes)); + + /* initialize RX queues */ + for (uint16_t queue_id = 0; queue_id < port_cfg->n_rxq; ++queue_id) { + plog_info("\t\tSetting up RX queue %u on port %u on socket %u with %u desc (pool 0x%p)\n", + queue_id, port_id, port_cfg->socket, + port_cfg->n_rxd, port_cfg->pool[queue_id]); + + ret = rte_eth_rx_queue_setup(port_id, queue_id, + port_cfg->n_rxd, + port_cfg->socket, &port_cfg->rx_conf, + port_cfg->pool[queue_id]); + + PROX_PANIC(ret < 0, "\t\t\trte_eth_rx_queue_setup() failed on port %u: error %s (%d)\n", port_id, strerror(-ret), ret); + } + if (!strcmp(port_cfg->short_name, "virtio")) { + port_cfg->tx_conf.txq_flags |= ETH_TXQ_FLAGS_NOOFFLOADS; + plog_info("\t\tDisabling TX offloads (virtio does not support TX offloads)\n"); + } + + if (!strcmp(port_cfg->short_name, "vmxnet3")) { + port_cfg->tx_conf.txq_flags |= ETH_TXQ_FLAGS_NOOFFLOADS | ETH_TXQ_FLAGS_NOMULTSEGS; + plog_info("\t\tDisabling TX offloads and multsegs on port %d as vmxnet3 does not support them\n", port_id); + } + /* initialize one TX queue per logical core on each port */ + for (uint16_t queue_id = 0; queue_id < port_cfg->n_txq; ++queue_id) { + plog_info("\t\tSetting up TX queue %u on socket %u with %u desc\n", + queue_id, port_cfg->socket, port_cfg->n_txd); + ret = rte_eth_tx_queue_setup(port_id, queue_id, port_cfg->n_txd, + port_cfg->socket, &port_cfg->tx_conf); + PROX_PANIC(ret < 0, "\t\t\trte_eth_tx_queue_setup() failed on port %u: error %d\n", port_id, ret); + } + + plog_info("\t\tStarting up port %u ...", port_id); + ret = rte_eth_dev_start(port_id); + + PROX_PANIC(ret < 0, "\n\t\t\trte_eth_dev_start() failed on port %u: error %d\n", port_id, ret); + plog_info(" done: "); + + /* Getting link status can be done without waiting if Link + State Interrupt is enabled since in that case, if the link + is recognized as being down, an interrupt will notify that + it has gone up. */ + if (port_cfg->port_conf.intr_conf.lsc) + rte_eth_link_get_nowait(port_id, &link); + else + rte_eth_link_get(port_id, &link); + + port_cfg->link_up = link.link_status; + port_cfg->link_speed = link.link_speed; + if (link.link_status) { + plog_info("Link Up - speed %'u Mbps - %s\n", + link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? + "full-duplex" : "half-duplex"); + } + else { + plog_info("Link Down\n"); + } + + if (port_cfg->promiscuous) { + rte_eth_promiscuous_enable(port_id); + plog_info("\t\tport %u in promiscuous mode\n", port_id); + } + + if (strcmp(port_cfg->short_name, "ixgbe_vf") && + strcmp(port_cfg->short_name, "i40e") && + strcmp(port_cfg->short_name, "i40e_vf") && + strcmp(port_cfg->short_name, "vmxnet3")) { + for (uint8_t i = 0; i < 16; ++i) { + ret = rte_eth_dev_set_rx_queue_stats_mapping(port_id, i, i); + if (ret) { + plog_info("\t\trte_eth_dev_set_rx_queue_stats_mapping() failed: error %d\n", ret); + } + ret = rte_eth_dev_set_tx_queue_stats_mapping(port_id, i, i); + if (ret) { + plog_info("\t\trte_eth_dev_set_tx_queue_stats_mapping() failed: error %d\n", ret); + } + } + } +} + +void init_port_all(void) +{ + uint8_t max_port_idx = prox_last_port_active() + 1; + + for (uint8_t portid = 0; portid < max_port_idx; ++portid) { + if (!prox_port_cfg[portid].active) { + continue; + } + init_port(&prox_port_cfg[portid]); + } +} + +void close_ports_atexit(void) +{ + uint8_t max_port_idx = prox_last_port_active() + 1; + + for (uint8_t portid = 0; portid < max_port_idx; ++portid) { + if (!prox_port_cfg[portid].active) { + continue; + } + rte_eth_dev_close(portid); + } +} + +void init_port_addr(void) +{ + struct prox_port_cfg *port_cfg; + + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + if (!prox_port_cfg[port_id].active) { + continue; + } + port_cfg = &prox_port_cfg[port_id]; + + switch (port_cfg->type) { + case PROX_PORT_MAC_HW: + rte_eth_macaddr_get(port_id, &port_cfg->eth_addr); + break; + case PROX_PORT_MAC_RAND: + eth_random_addr(port_cfg->eth_addr.addr_bytes); + break; + case PROX_PORT_MAC_SET: + break; + } + } +} + +int port_is_active(uint8_t port_id) +{ + if (port_id > PROX_MAX_PORTS) { + plog_info("requested port is higher than highest supported port ID (%u)\n", PROX_MAX_PORTS); + return 0; + } + + struct prox_port_cfg* port_cfg = &prox_port_cfg[port_id]; + if (!port_cfg->active) { + plog_info("Port %u is not active\n", port_id); + return 0; + } + return 1; +} diff --git a/VNFs/DPPD-PROX/prox_port_cfg.h b/VNFs/DPPD-PROX/prox_port_cfg.h new file mode 100644 index 00000000..17616187 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_port_cfg.h @@ -0,0 +1,83 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_PORT_CFG_H +#define _PROX_PORT_CFG_H + +#include <rte_ether.h> +#include <rte_ethdev.h> + +#include "prox_globals.h" + +enum addr_type {PROX_PORT_MAC_HW, PROX_PORT_MAC_SET, PROX_PORT_MAC_RAND}; + +#define IPV4_CKSUM 1 +#define UDP_CKSUM 2 + +struct prox_port_cfg { + struct rte_mempool *pool[32]; /* Rx/Tx mempool */ + size_t pool_size[32]; + uint8_t promiscuous; + uint8_t lsc_set_explicitely; /* Explicitly enable/disable lsc */ + uint8_t lsc_val; + uint8_t active; + int socket; + uint16_t max_rxq; /* max number of Tx queues */ + uint16_t max_txq; /* max number of Tx queues */ + uint16_t n_rxq; /* number of used Rx queues */ + uint16_t n_txq; /* number of used Tx queues */ + uint32_t n_rxd; + uint32_t n_txd; + uint8_t link_up; + uint32_t link_speed; + uint32_t mtu; + enum addr_type type; + struct ether_addr eth_addr; /* port MAC address */ + char name[MAX_NAME_SIZE]; + char short_name[MAX_NAME_SIZE]; + char driver_name[MAX_NAME_SIZE]; + char rx_ring[MAX_NAME_SIZE]; + char tx_ring[MAX_NAME_SIZE]; + char pci_addr[32]; + struct rte_eth_conf port_conf; + struct rte_eth_rxconf rx_conf; + struct rte_eth_txconf tx_conf; + struct { + int tx_offload_cksum; + } capabilities; +}; + +extern rte_atomic32_t lsc; + +int prox_nb_active_ports(void); +int prox_last_port_active(void); + +extern struct prox_port_cfg prox_port_cfg[]; + +void init_rte_dev(int use_dummy_devices); +uint8_t init_rte_ring_dev(void); +void init_port_addr(void); +void init_port_all(void); +void close_ports_atexit(void); + +struct rte_mempool; + +void prox_pktmbuf_init(struct rte_mempool *mp, void *opaque_arg, void *_m, unsigned i); +void prox_pktmbuf_reinit(void *arg, void *start, void *end, uint32_t idx); + +int port_is_active(uint8_t port_id); + +#endif /* __PROX_PORT_CFG_H_ */ diff --git a/VNFs/DPPD-PROX/prox_shared.c b/VNFs/DPPD-PROX/prox_shared.c new file mode 100644 index 00000000..890d564b --- /dev/null +++ b/VNFs/DPPD-PROX/prox_shared.c @@ -0,0 +1,174 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdio.h> +#include <rte_hash.h> +#include <rte_hash_crc.h> +#include <rte_version.h> + +#include "quit.h" +#include "log.h" +#include "prox_shared.h" +#include "prox_globals.h" + +#define INIT_HASH_TABLE_SIZE 8192 + +struct prox_shared { + struct rte_hash *hash; + size_t size; +}; + +struct prox_shared sh_system; +struct prox_shared sh_socket[MAX_SOCKETS]; +struct prox_shared sh_core[RTE_MAX_LCORE]; + +static char* get_sh_name(void) +{ + static char name[] = "prox_sh"; + + name[0]++; + return name; +} + +struct rte_hash_parameters param = { + .key_len = 256, + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + .socket_id = 0, +}; + +static void prox_sh_create_hash(struct prox_shared *ps, size_t size) +{ + param.entries = size; + param.name = get_sh_name(); + ps->hash = rte_hash_create(¶m); + PROX_PANIC(ps->hash == NULL, "Failed to create hash table for shared data"); + ps->size = size; + if (ps->size == INIT_HASH_TABLE_SIZE) + plog_info("Shared data tracking hash table created with size %zu\n", ps->size); + else + plog_info("Shared data tracking hash table grew to %zu\n", ps->size); +} + +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) +static int copy_hash(struct rte_hash *new_hash, struct rte_hash *old_hash) +{ + const void *next_key; + void *next_data; + uint32_t iter = 0; + + while (rte_hash_iterate(old_hash, &next_key, &next_data, &iter) >= 0) { + if (rte_hash_add_key_data(new_hash, next_key, next_data) < 0) + return -1; + } + + return 0; +} +#endif + +static int prox_sh_add(struct prox_shared *ps, const char *name, void *data) +{ + char key[256] = {0}; + int ret; + + strncpy(key, name, sizeof(key)); + if (ps->size == 0) { + prox_sh_create_hash(ps, INIT_HASH_TABLE_SIZE); + } + +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + do { + ret = rte_hash_add_key_data(ps->hash, key, data); + if (ret < 0) { + struct rte_hash *old = ps->hash; + int success; + do { + prox_sh_create_hash(ps, ps->size * 2); + success = !copy_hash(ps->hash, old); + if (success) + rte_hash_free(old); + else + rte_hash_free(ps->hash); + } while (!success); + } + } while (ret < 0); +#else + PROX_PANIC(1, "DPDK < 2.1 not fully supported"); +#endif + return 0; +} + +static void *prox_sh_find(struct prox_shared *sh, const char *name) +{ +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + char key[256] = {0}; + int ret; + void *data; + + if (!sh->hash) + return NULL; + + strncpy(key, name, sizeof(key)); + ret = rte_hash_lookup_data(sh->hash, key, &data); + if (ret >= 0) + return data; +#else + PROX_PANIC(1, "DPDK < 2.1 not fully supported"); +#endif + return NULL; +} + +int prox_sh_add_system(const char *name, void *data) +{ + return prox_sh_add(&sh_system, name, data); +} + +int prox_sh_add_socket(const int socket_id, const char *name, void *data) +{ + if (socket_id >= MAX_SOCKETS) + return -1; + + return prox_sh_add(&sh_socket[socket_id], name, data); +} + +int prox_sh_add_core(const int core_id, const char *name, void *data) +{ + if (core_id >= RTE_MAX_LCORE) + return -1; + + return prox_sh_add(&sh_core[core_id], name, data); +} + +void *prox_sh_find_system(const char *name) +{ + return prox_sh_find(&sh_system, name); +} + +void *prox_sh_find_socket(const int socket_id, const char *name) +{ + if (socket_id >= MAX_SOCKETS) + return NULL; + + return prox_sh_find(&sh_socket[socket_id], name); +} + +void *prox_sh_find_core(const int core_id, const char *name) +{ + if (core_id >= RTE_MAX_LCORE) + return NULL; + + return prox_sh_find(&sh_core[core_id], name); +} diff --git a/VNFs/DPPD-PROX/prox_shared.h b/VNFs/DPPD-PROX/prox_shared.h new file mode 100644 index 00000000..c98b1d64 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_shared.h @@ -0,0 +1,32 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROX_SHARED_H_ +#define _PROX_SHARED_H_ + +#include <rte_ether.h> + +/* Data can be shared at different levels. The levels are core wide, + socket wide and system wide. */ +int prox_sh_add_system(const char *name, void *data); +int prox_sh_add_socket(const int socket_id, const char *name, void *data); +int prox_sh_add_core(const int core_id, const char *name, void *data); + +void *prox_sh_find_system(const char *name); +void *prox_sh_find_socket(const int socket_id, const char *name); +void *prox_sh_find_core(const int core_id, const char *name); + +#endif /* _PROX_SHARED_H_ */ diff --git a/VNFs/DPPD-PROX/qinq.h b/VNFs/DPPD-PROX/qinq.h new file mode 100644 index 00000000..14da9753 --- /dev/null +++ b/VNFs/DPPD-PROX/qinq.h @@ -0,0 +1,40 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _QINQ_H_ +#define _QINQ_H_ + +#include <rte_ether.h> + +struct my_vlan_hdr { + uint16_t eth_proto; + uint16_t vlan_tci; +} __attribute__((packed)); + +struct vlans { + struct my_vlan_hdr svlan; + struct my_vlan_hdr cvlan; +}; + +struct qinq_hdr { + struct ether_addr d_addr; + struct ether_addr s_addr; + struct my_vlan_hdr svlan; + struct my_vlan_hdr cvlan; + uint16_t ether_type; +} __attribute__((packed)); + +#endif /* _QINQ_H_ */ diff --git a/VNFs/DPPD-PROX/quit.h b/VNFs/DPPD-PROX/quit.h new file mode 100644 index 00000000..a01c0a02 --- /dev/null +++ b/VNFs/DPPD-PROX/quit.h @@ -0,0 +1,46 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _QUIT_H_ +#define _QUIT_H_ + +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include <rte_debug.h> + +#include "display.h" +#include "prox_cfg.h" + +/* PROX_PANIC for checks that are possibly hit due to configuration or + when feature is not implemented. */ +/* Restore tty and abort if there is a problem */ +#define PROX_PANIC(cond, ...) do { \ + if (cond) { \ + plog_info(__VA_ARGS__); \ + display_end(); \ + if (prox_cfg.flags & DSF_DAEMON) { \ + pid_t ppid = getppid(); \ + plog_info("sending SIGUSR2 to %d\n", ppid);\ + kill(ppid, SIGUSR2); \ + } \ + rte_panic("PANIC at %s:%u, callstack:\n", \ + __FILE__, __LINE__); \ + } \ + } while (0) + +#endif /* _QUIT_H_ */ diff --git a/VNFs/DPPD-PROX/random.h b/VNFs/DPPD-PROX/random.h new file mode 100644 index 00000000..85508126 --- /dev/null +++ b/VNFs/DPPD-PROX/random.h @@ -0,0 +1,58 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +/* + This pseudorandom number generator is based on ref_xorshift128plus, + as implemented by reference_xorshift.h, which has been obtained + from https://sourceforge.net/projects/xorshift-cpp/ + + The licensing terms for reference_xorshift.h are reproduced below. + + // Written in 2014 by Ivo Doko (ivo.doko@gmail.com) + // based on code written by Sebastiano Vigna (vigna@acm.org) + // To the extent possible under law, the author has dedicated + // all copyright and related and neighboring rights to this + // software to the public domain worldwide. This software is + // distributed without any warranty. + // See <http://creativecommons.org/publicdomain/zero/1.0/>. +*/ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#include <rte_cycles.h> + +struct random { + uint64_t state[2]; +}; + +static void random_init_seed(struct random *random) +{ + random->state[0] = rte_rdtsc(); + random->state[1] = rte_rdtsc(); +} + +static uint64_t random_next(struct random *random) +{ + const uint64_t s0 = random->state[1]; + const uint64_t s1 = random->state[0] ^ (random->state[0] << 23); + + random->state[0] = random->state[1]; + random->state[1] = (s1 ^ (s1 >> 18) ^ s0 ^ (s0 >> 5)) + s0; + return random->state[1]; +} + +#endif /* _RANDOM_H_ */ diff --git a/VNFs/DPPD-PROX/run.c b/VNFs/DPPD-PROX/run.c new file mode 100644 index 00000000..971d7148 --- /dev/null +++ b/VNFs/DPPD-PROX/run.c @@ -0,0 +1,241 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <string.h> + +#include <rte_launch.h> +#include <rte_cycles.h> +#include <rte_atomic.h> + +#include "run.h" +#include "prox_cfg.h" +#include "prox_port_cfg.h" +#include "quit.h" +#include "commands.h" +#include "main.h" +#include "log.h" +#include "display.h" +#include "stats.h" +#include "stats_cons.h" +#include "stats_cons_log.h" +#include "stats_cons_cli.h" + +#include "input.h" +#include "input_curses.h" +#include "input_conn.h" + +static int needs_refresh; +static uint64_t update_interval; +static int stop_prox = 0; /* set to 1 to stop prox */ + +void set_update_interval(uint32_t msec) +{ + update_interval = msec_to_tsc(msec); +} + +void req_refresh(void) +{ + needs_refresh = 1; +} + +void quit(void) +{ + static rte_atomic32_t already_leaving = RTE_ATOMIC32_INIT(0); + if (!rte_atomic32_test_and_set(&already_leaving)) + return; + + plog_info("Leaving...\n"); + if (lcore_cfg == NULL) + exit(EXIT_SUCCESS); + stop_core_all(-1); + stop_prox = 1; +} + +static void update_link_states(void) +{ + struct prox_port_cfg *port_cfg; + struct rte_eth_link link; + + for (uint8_t portid = 0; portid < PROX_MAX_PORTS; ++portid) { + if (!prox_port_cfg[portid].active) { + continue; + } + + port_cfg = &prox_port_cfg[portid]; + rte_eth_link_get_nowait(portid, &link); + port_cfg->link_up = link.link_status; + port_cfg->link_speed = link.link_speed; + } +} + +static struct stats_cons stats_cons[8]; +static size_t n_stats_cons = 0; +static uint16_t stats_cons_flags = 0; + +static void stats_cons_add(struct stats_cons *sc) +{ + if (n_stats_cons == sizeof(stats_cons)/sizeof(stats_cons[0])) + return; + + stats_cons[n_stats_cons++] = *sc; + sc->init(); + stats_cons_flags |= sc->flags; +} + +static void stats_cons_notify(void) +{ + for (size_t i = 0; i < n_stats_cons; ++i) { + stats_cons[i].notify(); + } +} + +static void stats_cons_refresh(void) +{ + for (size_t i = 0; i < n_stats_cons; ++i) { + if (stats_cons[i].refresh) + stats_cons[i].refresh(); + } +} + +static void stats_cons_finish(void) +{ + for (size_t i = 0; i < n_stats_cons; ++i) { + if (stats_cons[i].finish) + stats_cons[i].finish(); + } +} + +static void busy_wait_until(uint64_t deadline) +{ + while (rte_rdtsc() < deadline) + ; +} + +static void multiplexed_input_stats(uint64_t deadline) +{ + input_proc_until(deadline); + + if (needs_refresh) { + needs_refresh = 0; + stats_cons_refresh(); + } + + if (rte_atomic32_read(&lsc)) { + rte_atomic32_dec(&lsc); + update_link_states(); + stats_cons_refresh(); + } +} + +static void print_warnings(void) +{ + if (get_n_warnings() == -1) { + plog_info("Warnings disabled\n"); + } + else if (get_n_warnings() > 0) { + int n_print = get_n_warnings() < 5? get_n_warnings(): 5; + plog_info("Started with %d warnings, last %d warnings: \n", get_n_warnings(), n_print); + for (int i = -n_print + 1; i <= 0; ++i) { + plog_info("%s", get_warning(i)); + } + } + else { + plog_info("Started without warnings\n"); + } +} + +/* start main loop */ +void __attribute__((noreturn)) run(uint32_t flags) +{ + uint64_t cur_tsc; + uint64_t next_update; + uint64_t stop_tsc = 0; + const uint64_t update_interval_threshold = usec_to_tsc(1); + + if (flags & DSF_LISTEN_TCP) + PROX_PANIC(reg_input_tcp(), "Failed to start listening on TCP port 8474: %s\n", strerror(errno)); + if (flags & DSF_LISTEN_UDS) + PROX_PANIC(reg_input_uds(), "Failed to start listening on UDS /tmp/prox.sock: %s\n", strerror(errno)); + + if (prox_cfg.use_stats_logger) + stats_cons_add(stats_cons_log_get()); + + stats_init(prox_cfg.start_time, prox_cfg.duration_time); + stats_update(STATS_CONS_F_ALL); + + switch (prox_cfg.ui) { + case PROX_UI_CURSES: + reg_input_curses(); + stats_cons_add(&display); + break; + case PROX_UI_CLI: + stats_cons_add(stats_cons_cli_get()); + break; + case PROX_UI_NONE: + default: + break; + } + + if (flags & DSF_AUTOSTART) + start_core_all(-1); + else + stop_core_all(-1); + + cur_tsc = rte_rdtsc(); + if (prox_cfg.duration_time != 0) { + stop_tsc = cur_tsc + sec_to_tsc(prox_cfg.start_time + prox_cfg.duration_time); + } + + stats_cons_notify(); + stats_cons_refresh(); + + update_interval = str_to_tsc(prox_cfg.update_interval_str); + next_update = cur_tsc + update_interval; + + cmd_rx_tx_info(); + print_warnings(); + + while (stop_prox == 0) { + + if (update_interval < update_interval_threshold) + busy_wait_until(next_update); + else + multiplexed_input_stats(next_update); + + next_update += update_interval; + + stats_update(stats_cons_flags); + stats_cons_notify(); + + if (stop_tsc && rte_rdtsc() >= stop_tsc) { + stop_prox = 1; + } + } + + stats_cons_finish(); + + if (prox_cfg.flags & DSF_WAIT_ON_QUIT) { + stop_core_all(-1); + } + + if (prox_cfg.logbuf) { + file_print(prox_cfg.logbuf); + } + + display_end(); + exit(EXIT_SUCCESS); +} diff --git a/VNFs/DPPD-PROX/run.h b/VNFs/DPPD-PROX/run.h new file mode 100644 index 00000000..3c61aca6 --- /dev/null +++ b/VNFs/DPPD-PROX/run.h @@ -0,0 +1,25 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _RUN_H_ +#define _RUN_H_ + +void run(uint32_t flags); +void quit(void); +void req_refresh(void); +void set_update_interval(uint32_t msec); + +#endif /* _RUN_H_ */ diff --git a/VNFs/DPPD-PROX/rw_reg.c b/VNFs/DPPD-PROX/rw_reg.c new file mode 100644 index 00000000..a0e59085 --- /dev/null +++ b/VNFs/DPPD-PROX/rw_reg.c @@ -0,0 +1,36 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ethdev.h> +#include "rw_reg.h" + +int read_reg(uint8_t port_id, uint32_t addr, uint32_t *reg) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + struct _dev_hw *hw = (struct _dev_hw *)dev->data->dev_private; + + *reg = PROX_READ_REG(hw, addr); + return 0; +} + +int write_reg(uint8_t port_id, uint32_t reg, uint32_t val) +{ + struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + struct _dev_hw *hw = (struct _dev_hw *)dev->data->dev_private; + + PROX_WRITE_REG(hw, reg, val); + return 0; +} diff --git a/VNFs/DPPD-PROX/rw_reg.h b/VNFs/DPPD-PROX/rw_reg.h new file mode 100644 index 00000000..1e38c7d0 --- /dev/null +++ b/VNFs/DPPD-PROX/rw_reg.h @@ -0,0 +1,43 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef __RW_REG_H__ +#define __RW_REG_H__ + +/* Simplified, from DPDK 1.8 */ +struct _dev_hw { + uint8_t *hw_addr; +}; +/* Registers access */ + +#define PROX_PCI_REG_ADDR(hw, reg) \ + ((volatile uint32_t *)((char *)(hw)->hw_addr + (reg))) +#define PROX_READ_REG(hw, reg) \ + prox_read_addr(PROX_PCI_REG_ADDR((hw), (reg))) +#define PROX_PCI_REG(reg) (*((volatile uint32_t *)(reg))) +#define PROX_PCI_REG_WRITE(reg_addr, value) \ + *((volatile uint32_t *) (reg_addr)) = (value) +#define PROX_WRITE_REG(hw,reg,value) \ + PROX_PCI_REG_WRITE(PROX_PCI_REG_ADDR((hw), (reg)), (value)) + +static inline uint32_t prox_read_addr(volatile void* addr) +{ + return rte_le_to_cpu_32(PROX_PCI_REG(addr)); +} + +int read_reg(uint8_t portid, uint32_t addr, uint32_t *reg); +int write_reg(uint8_t portid, uint32_t reg, uint32_t val); +#endif diff --git a/VNFs/DPPD-PROX/rx_pkt.c b/VNFs/DPPD-PROX/rx_pkt.c new file mode 100644 index 00000000..a6c1fd10 --- /dev/null +++ b/VNFs/DPPD-PROX/rx_pkt.c @@ -0,0 +1,427 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_ethdev.h> +#include <rte_version.h> + +#include "rx_pkt.h" +#include "task_base.h" +#include "clock.h" +#include "stats.h" +#include "log.h" +#include "mbuf_utils.h" +#include "input.h" /* Needed for callback on dump */ + +/* _param version of the rx_pkt_hw functions are used to create two + instances of very similar variations of these functions. The + variations are specified by the "multi" parameter which significies + that the rte_eth_rx_burst function should be called multiple times. + The reason for this is that with the vector PMD, the maximum number + of packets being returned is 32. If packets have been split in + multiple mbufs then rte_eth_rx_burst might even receive less than + 32 packets. + Some algorithms (like QoS) only work correctly if more than 32 + packets are received if the dequeue step involves finding 32 packets. +*/ + +#define MIN_PMD_RX 32 + +static uint16_t rx_pkt_hw_port_queue(struct port_queue *pq, struct rte_mbuf **mbufs, int multi) +{ + uint16_t nb_rx, n; + + nb_rx = rte_eth_rx_burst(pq->port, pq->queue, mbufs, MAX_PKT_BURST); + + if (multi) { + n = nb_rx; + while (n != 0 && MAX_PKT_BURST - nb_rx >= MIN_PMD_RX) { + n = rte_eth_rx_burst(pq->port, pq->queue, mbufs + nb_rx, MIN_PMD_RX); + nb_rx += n; + PROX_PANIC(nb_rx > 64, "Received %d packets while expecting maximum %d\n", n, MIN_PMD_RX); + } + } + return nb_rx; +} + +static void next_port(struct rx_params_hw *rx_params_hw) +{ + ++rx_params_hw->last_read_portid; + if (unlikely(rx_params_hw->last_read_portid == rx_params_hw->nb_rxports)) { + rx_params_hw->last_read_portid = 0; + } +} + +static void next_port_pow2(struct rx_params_hw *rx_params_hw) +{ + rx_params_hw->last_read_portid = (rx_params_hw->last_read_portid + 1) & rx_params_hw->rxport_mask; +} + +static uint16_t rx_pkt_hw_param(struct task_base *tbase, struct rte_mbuf ***mbufs, int multi, + void (*next)(struct rx_params_hw *rx_param_hw)) +{ + uint8_t last_read_portid; + uint16_t nb_rx; + + START_EMPTY_MEASSURE(); + *mbufs = tbase->ws_mbuf->mbuf[0] + + (RTE_ALIGN_CEIL(tbase->ws_mbuf->idx[0].prod, 2) & WS_MBUF_MASK); + + last_read_portid = tbase->rx_params_hw.last_read_portid; + struct port_queue *pq = &tbase->rx_params_hw.rx_pq[last_read_portid]; + + nb_rx = rx_pkt_hw_port_queue(pq, *mbufs, multi); + next(&tbase->rx_params_hw); + + if (likely(nb_rx > 0)) { + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; +} + +static inline uint16_t rx_pkt_hw1_param(struct task_base *tbase, struct rte_mbuf ***mbufs, int multi) +{ + uint16_t nb_rx, n; + + START_EMPTY_MEASSURE(); + *mbufs = tbase->ws_mbuf->mbuf[0] + + (RTE_ALIGN_CEIL(tbase->ws_mbuf->idx[0].prod, 2) & WS_MBUF_MASK); + + nb_rx = rte_eth_rx_burst(tbase->rx_params_hw1.rx_pq.port, + tbase->rx_params_hw1.rx_pq.queue, + *mbufs, MAX_PKT_BURST); + + if (multi) { + n = nb_rx; + while ((n != 0) && (MAX_PKT_BURST - nb_rx >= MIN_PMD_RX)) { + n = rte_eth_rx_burst(tbase->rx_params_hw1.rx_pq.port, + tbase->rx_params_hw1.rx_pq.queue, + *mbufs + nb_rx, MIN_PMD_RX); + nb_rx += n; + PROX_PANIC(nb_rx > 64, "Received %d packets while expecting maximum %d\n", n, MIN_PMD_RX); + } + } + + if (likely(nb_rx > 0)) { + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; +} + +uint16_t rx_pkt_hw(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw_param(tbase, mbufs, 0, next_port); +} + +uint16_t rx_pkt_hw_pow2(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw_param(tbase, mbufs, 0, next_port_pow2); +} + +uint16_t rx_pkt_hw1(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw1_param(tbase, mbufs, 0); +} + +uint16_t rx_pkt_hw_multi(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw_param(tbase, mbufs, 1, next_port); +} + +uint16_t rx_pkt_hw_pow2_multi(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw_param(tbase, mbufs, 1, next_port_pow2); +} + +uint16_t rx_pkt_hw1_multi(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + return rx_pkt_hw1_param(tbase, mbufs, 1); +} + +/* The following functions implement ring access */ +static uint16_t ring_deq(struct rte_ring *r, struct rte_mbuf **mbufs) +{ + void **v_mbufs = (void **)mbufs; +#ifdef BRAS_RX_BULK +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + return rte_ring_sc_dequeue_bulk(r, v_mbufs, MAX_RING_BURST) < 0? 0 : MAX_RING_BURST; +#else + return rte_ring_sc_dequeue_bulk(r, v_mbufs, MAX_RING_BURST, NULL); +#endif +#else +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + return rte_ring_sc_dequeue_burst(r, v_mbufs, MAX_RING_BURST); +#else + return rte_ring_sc_dequeue_burst(r, v_mbufs, MAX_RING_BURST, NULL); +#endif +#endif +} + +uint16_t rx_pkt_sw(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + START_EMPTY_MEASSURE(); + *mbufs = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + uint8_t lr = tbase->rx_params_sw.last_read_ring; + uint16_t nb_rx; + + do { + nb_rx = ring_deq(tbase->rx_params_sw.rx_rings[lr], *mbufs); + lr = lr + 1 == tbase->rx_params_sw.nb_rxrings? 0 : lr + 1; + } while(!nb_rx && lr != tbase->rx_params_sw.last_read_ring); + + tbase->rx_params_sw.last_read_ring = lr; + + if (nb_rx != 0) { + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + else { + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; + } +} + +/* Same as rx_pkt_sw expect with a mask for the number of receive + rings (can only be used if nb_rxring is a power of 2). */ +uint16_t rx_pkt_sw_pow2(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + START_EMPTY_MEASSURE(); + *mbufs = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + uint8_t lr = tbase->rx_params_sw.last_read_ring; + uint16_t nb_rx; + + do { + nb_rx = ring_deq(tbase->rx_params_sw.rx_rings[lr], *mbufs); + lr = (lr + 1) & tbase->rx_params_sw.rxrings_mask; + } while(!nb_rx && lr != tbase->rx_params_sw.last_read_ring); + + tbase->rx_params_sw.last_read_ring = lr; + + if (nb_rx != 0) { + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + else { + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; + } +} + +uint16_t rx_pkt_self(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + START_EMPTY_MEASSURE(); + uint16_t nb_rx = tbase->ws_mbuf->idx[0].nb_rx; + if (nb_rx) { + tbase->ws_mbuf->idx[0].nb_rx = 0; + *mbufs = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + else { + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; + } +} + +/* Used for tasks that do not receive packets (i.e. Packet +generation). Always returns 1 but never returns packets and does not +increment statistics. This function allows to use the same code path +as for tasks that actually receive packets. */ +uint16_t rx_pkt_dummy(__attribute__((unused)) struct task_base *tbase, + __attribute__((unused)) struct rte_mbuf ***mbufs) +{ + return 1; +} + +/* After the system has been configured, it is known if there is only + one RX ring. If this is the case, a more specialized version of the + function above can be used to save cycles. */ +uint16_t rx_pkt_sw1(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + START_EMPTY_MEASSURE(); + *mbufs = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + uint16_t nb_rx = ring_deq(tbase->rx_params_sw1.rx_ring, *mbufs); + + if (nb_rx != 0) { + TASK_STATS_ADD_RX(&tbase->aux->stats, nb_rx); + return nb_rx; + } + else { + TASK_STATS_ADD_IDLE(&tbase->aux->stats, rte_rdtsc() - cur_tsc); + return 0; + } +} + +static uint16_t call_prev_rx_pkt(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t ret; + + if (tbase->aux->rx_prev_idx + 1 == tbase->aux->rx_prev_count) { + ret = tbase->aux->rx_pkt_prev[tbase->aux->rx_prev_idx](tbase, mbufs); + } else { + tbase->aux->rx_prev_idx++; + ret = tbase->aux->rx_pkt_prev[tbase->aux->rx_prev_idx](tbase, mbufs); + tbase->aux->rx_prev_idx--; + } + + return ret; +} + +/* Only used when there are packets to be dumped. This function is + meant as a debugging tool and is therefore not optimized. When the + number of packets to dump falls back to 0, the original (optimized) + rx function is restored. This allows to support dumping packets + without any performance impact if the feature is not used. */ +uint16_t rx_pkt_dump(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t ret = call_prev_rx_pkt(tbase, mbufs); + + if (ret) { + uint32_t n_dump = tbase->aux->task_rt_dump.n_print_rx; + n_dump = ret < n_dump? ret : n_dump; + + if (tbase->aux->task_rt_dump.input->reply == NULL) { + for (uint32_t i = 0; i < n_dump; ++i) { + plogd_info((*mbufs)[i], "RX: "); + } + } + else { + struct input *input = tbase->aux->task_rt_dump.input; + + for (uint32_t i = 0; i < n_dump; ++i) { + /* TODO: Execute callback with full + data in a single call. */ + char tmp[128]; + int strlen; + +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + int port_id = ((*mbufs)[i])->port; +#else + int port_id = ((*mbufs)[i])->pkt.in_port; +#endif + strlen = snprintf(tmp, sizeof(tmp), "pktdump,%d,%d\n", port_id, + rte_pktmbuf_pkt_len((*mbufs)[i])); + + input->reply(input, tmp, strlen); + input->reply(input, rte_pktmbuf_mtod((*mbufs)[i], char *), rte_pktmbuf_pkt_len((*mbufs)[i])); + input->reply(input, "\n", 1); + } + } + + tbase->aux->task_rt_dump.n_print_rx -= n_dump; + + if (0 == tbase->aux->task_rt_dump.n_print_rx) { + task_base_del_rx_pkt_function(tbase, rx_pkt_dump); + } + } + return ret; +} + +uint16_t rx_pkt_trace(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t ret = call_prev_rx_pkt(tbase, mbufs); + + if (ret) { + uint32_t n_trace = tbase->aux->task_rt_dump.n_trace; + n_trace = ret < n_trace? ret : n_trace; + tbase->aux->task_rt_dump.cur_trace = n_trace; + + for (uint32_t i = 0; i < n_trace; ++i) { + uint8_t *pkt = rte_pktmbuf_mtod((*mbufs)[i], uint8_t *); + rte_memcpy(tbase->aux->task_rt_dump.pkt_cpy[i], pkt, sizeof(tbase->aux->task_rt_dump.pkt_cpy[i])); + tbase->aux->task_rt_dump.pkt_cpy_len[i] = rte_pktmbuf_pkt_len((*mbufs)[i]); + tbase->aux->task_rt_dump.pkt_mbuf_addr[i] = (*mbufs)[i]; + } + + tbase->aux->task_rt_dump.n_trace -= n_trace; + /* Unset by TX when n_trace = 0 */ + } + return ret; +} + +/* Gather the distribution of the number of packets that have been + received from one RX call. Since the value is only modified by the + task that receives the packet, no atomic operation is needed. */ +uint16_t rx_pkt_distr(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t ret = call_prev_rx_pkt(tbase, mbufs); + + tbase->aux->rx_bucket[ret]++; + return ret; +} + +uint16_t rx_pkt_bw(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t ret = call_prev_rx_pkt(tbase, mbufs); + uint32_t tot_bytes = 0; + + for (uint16_t i = 0; i < ret; ++i) { + tot_bytes += mbuf_wire_size((*mbufs)[i]); + } + + TASK_STATS_ADD_RX_BYTES(&tbase->aux->stats, tot_bytes); + + return ret; +} + +uint16_t rx_pkt_tsc(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint64_t before = rte_rdtsc(); + uint16_t ret = call_prev_rx_pkt(tbase, mbufs); + uint64_t after = rte_rdtsc(); + + tbase->aux->tsc_rx.before = before; + tbase->aux->tsc_rx.after = after; + + return ret; +} + +uint16_t rx_pkt_all(struct task_base *tbase, struct rte_mbuf ***mbufs) +{ + uint16_t tot = 0; + uint16_t ret = 0; + struct rte_mbuf **new_mbufs; + struct rte_mbuf **dst = tbase->aux->all_mbufs; + + /* In case we receive less than MAX_PKT_BURST packets in one + iteration, do no perform any copying of mbuf pointers. Use + the buffer itself instead. */ + ret = call_prev_rx_pkt(tbase, &new_mbufs); + if (ret < MAX_PKT_BURST/2) { + *mbufs = new_mbufs; + return ret; + } + + memcpy(dst + tot, new_mbufs, ret * sizeof(*dst)); + tot += ret; + *mbufs = dst; + + do { + ret = call_prev_rx_pkt(tbase, &new_mbufs); + memcpy(dst + tot, new_mbufs, ret * sizeof(*dst)); + tot += ret; + } while (ret == MAX_PKT_BURST/2 && tot < MAX_RX_PKT_ALL - MAX_PKT_BURST); + + if (tot >= MAX_RX_PKT_ALL - MAX_PKT_BURST) { + plog_err("Could not receive all packets - buffer full\n"); + } + + return tot; +} diff --git a/VNFs/DPPD-PROX/rx_pkt.h b/VNFs/DPPD-PROX/rx_pkt.h new file mode 100644 index 00000000..57b948e2 --- /dev/null +++ b/VNFs/DPPD-PROX/rx_pkt.h @@ -0,0 +1,49 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _RX_PKT_H_ +#define _RX_PKT_H_ + +#include <inttypes.h> + +struct rte_mbuf; +struct task_base; + +uint16_t rx_pkt_hw(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_hw_pow2(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_hw1(struct task_base *tbase, struct rte_mbuf ***mbufs); + +/* The _multi variation of the function is used to work-around the + problem with QoS, multi-seg mbufs and vector PMD. When vector + PMD returns more than 32 packets, the two variations of the + receive function can be merged back together. */ +uint16_t rx_pkt_hw_multi(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_hw_pow2_multi(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_hw1_multi(struct task_base *tbase, struct rte_mbuf ***mbufs); + +uint16_t rx_pkt_sw(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_sw_pow2(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_sw1(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_self(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_dummy(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_dump(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_trace(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_distr(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_bw(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_tsc(struct task_base *tbase, struct rte_mbuf ***mbufs); +uint16_t rx_pkt_all(struct task_base *tbase, struct rte_mbuf ***mbufs); + +#endif /* _RX_PKT_H_ */ diff --git a/VNFs/DPPD-PROX/stats.c b/VNFs/DPPD-PROX/stats.c new file mode 100644 index 00000000..2418826f --- /dev/null +++ b/VNFs/DPPD-PROX/stats.c @@ -0,0 +1,100 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "prox_malloc.h" +#include "prox_cfg.h" +#include "stats.h" +#include "stats_port.h" +#include "stats_mempool.h" +#include "stats_ring.h" +#include "stats_l4gen.h" +#include "stats_latency.h" +#include "stats_global.h" +#include "stats_core.h" +#include "stats_task.h" +#include "stats_prio_task.h" +#include "stats_latency.h" + +/* Stores all readed values from the cores, displaying is done afterwards because + displaying introduces overhead. If displaying was done right after the values + are read, inaccuracy is introduced for later cores */ +int last_stat; /* 0 or 1 to track latest 2 measurements */ + +void stats_reset(void) +{ + stats_task_reset(); + stats_prio_task_reset(); + stats_port_reset(); + stats_latency_reset(); + stats_global_reset(); +} + +void stats_init(unsigned avg_start, unsigned duration) +{ + stats_lcore_init(); + stats_task_init(); + stats_prio_task_init(); + stats_port_init(); + stats_mempool_init(); + stats_latency_init(); + stats_l4gen_init(); + stats_ring_init(); + stats_global_init(avg_start, duration); +} + +void stats_update(uint16_t flag_cons) +{ + /* Keep track of last 2 measurements. */ + last_stat = !last_stat; + + if (flag_cons & STATS_CONS_F_TASKS) + stats_task_update(); + + if (flag_cons & STATS_CONS_F_PRIO_TASKS) + stats_prio_task_update(); + + if (flag_cons & STATS_CONS_F_LCORE) + stats_lcore_update(); + + if (flag_cons & STATS_CONS_F_PORTS) + stats_port_update(); + + if (flag_cons & STATS_CONS_F_MEMPOOLS) + stats_mempool_update(); + + if (flag_cons & STATS_CONS_F_LATENCY) + stats_latency_update(); + + if (flag_cons & STATS_CONS_F_L4GEN) + stats_l4gen_update(); + + if (flag_cons & STATS_CONS_F_RINGS) + stats_ring_update(); + + if (flag_cons & STATS_CONS_F_LCORE) + stats_lcore_post_proc(); + + if (flag_cons & STATS_CONS_F_TASKS) + stats_task_post_proc(); + + if (flag_cons & STATS_CONS_F_PRIO_TASKS) + stats_prio_task_post_proc(); + + if (flag_cons & STATS_CONS_F_GLOBAL) + stats_global_post_proc(); +} diff --git a/VNFs/DPPD-PROX/stats.h b/VNFs/DPPD-PROX/stats.h new file mode 100644 index 00000000..382fc4d0 --- /dev/null +++ b/VNFs/DPPD-PROX/stats.h @@ -0,0 +1,31 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_H_ +#define _STATS_H_ + +#include <rte_atomic.h> + +#include "stats_cons.h" +#include "clock.h" +#include "prox_globals.h" +#include "genl4_bundle.h" + +void stats_reset(void); +void stats_init(unsigned avg_start, unsigned duration); +void stats_update(uint16_t flag_cons); + +#endif /* _STATS_H_ */ diff --git a/VNFs/DPPD-PROX/stats_cons.h b/VNFs/DPPD-PROX/stats_cons.h new file mode 100644 index 00000000..ba51f49f --- /dev/null +++ b/VNFs/DPPD-PROX/stats_cons.h @@ -0,0 +1,39 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_CONS_H_ +#define _STATS_CONS_H_ + +#define STATS_CONS_F_TASKS 0x01 +#define STATS_CONS_F_LCORE 0x02 +#define STATS_CONS_F_PORTS 0x04 +#define STATS_CONS_F_MEMPOOLS 0x08 +#define STATS_CONS_F_RINGS 0x10 +#define STATS_CONS_F_LATENCY 0x20 +#define STATS_CONS_F_L4GEN 0x40 +#define STATS_CONS_F_GLOBAL 0x80 +#define STATS_CONS_F_PRIO_TASKS 0x100 +#define STATS_CONS_F_ALL 0x1ff + +struct stats_cons { + void (*init)(void); + void (*notify)(void); + void (*refresh)(void); /* Only called if not NULL, used to signal lsc or core stop/start */ + void (*finish)(void); + uint16_t flags; +}; + +#endif /* _STATS_CONS_H_ */ diff --git a/VNFs/DPPD-PROX/stats_cons_cli.c b/VNFs/DPPD-PROX/stats_cons_cli.c new file mode 100644 index 00000000..4f9235c6 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_cons_cli.c @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "stats.h" +#include "stats_cons_cli.h" +#include "prox_cfg.h" +#include "prox_args.h" +#include "prox_assert.h" +#include "commands.h" + +static struct stats_cons stats_cons_cli = { + .init = stats_cons_cli_init, + .notify = stats_cons_cli_notify, + .finish = stats_cons_cli_finish, + .flags = STATS_CONS_F_ALL, +}; + +struct stats_cons *stats_cons_cli_get(void) +{ + return &stats_cons_cli; +} + +void stats_cons_cli_init(void) +{ +} + +void stats_cons_cli_notify(void) +{ +} + +void stats_cons_cli_finish(void) +{ +} diff --git a/VNFs/DPPD-PROX/stats_cons_cli.h b/VNFs/DPPD-PROX/stats_cons_cli.h new file mode 100644 index 00000000..b5856d6d --- /dev/null +++ b/VNFs/DPPD-PROX/stats_cons_cli.h @@ -0,0 +1,28 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_CONS_CLI_H_ +#define _STATS_CONS_CLI_H_ + +#include "stats_cons.h" + +void stats_cons_cli_init(void); +void stats_cons_cli_notify(void); +void stats_cons_cli_finish(void); + +struct stats_cons *stats_cons_cli_get(void); + +#endif /* _STATS_CONS_CLI_H_ */ diff --git a/VNFs/DPPD-PROX/stats_cons_log.c b/VNFs/DPPD-PROX/stats_cons_log.c new file mode 100644 index 00000000..7e966533 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_cons_log.c @@ -0,0 +1,269 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "stats.h" +#include "stats_l4gen.h" +#include "stats_cons_log.h" +#include "prox_cfg.h" +#include "prox_args.h" +#include "prox_assert.h" +#include "commands.h" + +static struct stats_cons stats_cons_log = { + .init = stats_cons_log_init, + .notify = stats_cons_log_notify, + .finish = stats_cons_log_finish, +#ifndef DPI_STATS + .flags = STATS_CONS_F_ALL, +#else + .flags = STATS_CONS_F_PORTS|STATS_CONS_F_TASKS, +#endif +}; + +struct header { + uint64_t hz; + uint64_t now; + uint64_t n_entries; + uint64_t n_entry_fields; + uint8_t n_entry_field_size[64]; +}; + +static void header_init(struct header *hdr, uint64_t hz, uint64_t now, uint64_t n_entries) { + memset(hdr, 0, sizeof(*hdr)); + hdr->hz = hz; + hdr->now = now; + hdr->n_entries = n_entries; +} + +static void header_add_field(struct header *hdr, uint8_t size) { + hdr->n_entry_field_size[hdr->n_entry_fields++] = size; +} + +static void header_write(struct header *hdr, FILE *fp) { + size_t header_size_no_fields = sizeof(*hdr) - sizeof(hdr->n_entry_field_size); + size_t header_size_effective = header_size_no_fields + hdr->n_entry_fields; + + fwrite(hdr, header_size_effective, 1, fp); +} + +#define BUFFERED_RECORD_LEN 16384 + +#define STATS_DUMP_FILE_NAME "stats_dump" +static FILE *fp; + +struct entry { + uint32_t lcore_id; + uint32_t task_id; +#ifndef DPI_STATS + uint32_t l4_stats_id; +#endif +}; + +static struct entry entries[64]; +static uint64_t n_entries; + +#ifndef DPI_STATS +struct record { + uint32_t lcore_id; + uint32_t task_id; + uint64_t active_connections; + uint64_t bundles_created; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t tsc; +} __attribute__((packed)); +#else +struct record { + uint32_t lcore_id; + uint32_t task_id; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t drop_bytes; + uint64_t tsc; +} __attribute__((packed)); +#endif + +static struct record buf[BUFFERED_RECORD_LEN]; +static size_t buf_pos = 0; + +struct stats_cons *stats_cons_log_get(void) +{ + return &stats_cons_log; +} + +#ifndef DPI_STATS +void stats_cons_log_init(void) +{ + fp = fopen(STATS_DUMP_FILE_NAME, "w"); + if (!fp) + return; + + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (lconf->n_tasks_all && (strcmp(lconf->targs[0].task_init->mode_str, "genl4") || + strcmp(lconf->targs[0].task_init->sub_mode_str, ""))) + continue; + + for (uint32_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + entries[n_entries].lcore_id = lcore_id; + entries[n_entries].task_id = task_id; + entries[n_entries].l4_stats_id = n_entries; + n_entries++; + if (n_entries == sizeof(entries)/sizeof(entries[0])) + break; + } + cmd_rx_bw_start(lcore_id); + cmd_tx_bw_start(lcore_id); + if (n_entries == sizeof(entries)/sizeof(entries[0])) + break; + } + + struct header hdr; + + header_init(&hdr, rte_get_tsc_hz(), rte_rdtsc(), n_entries); + header_add_field(&hdr, sizeof(((struct record *)0)->lcore_id)); + header_add_field(&hdr, sizeof(((struct record *)0)->task_id)); + header_add_field(&hdr, sizeof(((struct record *)0)->active_connections)); + header_add_field(&hdr, sizeof(((struct record *)0)->bundles_created)); + header_add_field(&hdr, sizeof(((struct record *)0)->rx_bytes)); + header_add_field(&hdr, sizeof(((struct record *)0)->tx_bytes)); + header_add_field(&hdr, sizeof(((struct record *)0)->tsc)); + + header_write(&hdr, fp); +} + +void stats_cons_log_notify(void) +{ + const uint32_t n_l4gen = stats_get_n_l4gen(); + + if (buf_pos + n_entries > sizeof(buf)/sizeof(buf[0])) { + fwrite(buf, sizeof(buf[0]), buf_pos, fp); + buf_pos = 0; + } + PROX_ASSERT(buf_pos + n_entries <= sizeof(buf)/sizeof(buf[0])); + + for (uint32_t i = 0; i < n_entries; ++i) { + uint32_t c = entries[i].lcore_id; + uint32_t t = entries[i].task_id; + uint32_t j = entries[i].l4_stats_id; + struct l4_stats_sample *clast = stats_get_l4_stats_sample(j, 1); + struct task_stats *l = stats_get_task_stats(c, t); + struct task_stats_sample *last = stats_get_task_stats_sample(c, t, 1); + + buf[buf_pos].lcore_id = c; + buf[buf_pos].task_id = t; + + uint64_t tot_created = clast->stats.tcp_created + clast->stats.udp_created; + uint64_t tot_finished = clast->stats.tcp_finished_retransmit + clast->stats.tcp_finished_no_retransmit + + clast->stats.udp_finished + clast->stats.udp_expired + clast->stats.tcp_expired; + + buf[buf_pos].active_connections = tot_created - tot_finished; + buf[buf_pos].bundles_created = clast->stats.bundles_created; + buf[buf_pos].rx_bytes = last->rx_bytes; + buf[buf_pos].tx_bytes = last->tx_bytes; + buf[buf_pos].tsc = clast->tsc; + + buf_pos++; + } +} + +#else +void stats_cons_log_init(void) +{ + uint64_t el = rte_get_tsc_hz(); + uint64_t now = rte_rdtsc(); + + fp = fopen(STATS_DUMP_FILE_NAME, "w"); + if (!fp) + return; + + uint32_t lcore_id = -1; + + while(prox_core_next(&lcore_id, 0) == 0) { + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + if (!lconf->n_tasks_all) + continue; + + for (uint32_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + if (strcmp(lconf->targs[task_id].task_init->mode_str, "lbpos")) + continue; + + entries[n_entries].lcore_id = lcore_id; + entries[n_entries].task_id = task_id; + n_entries++; + if (n_entries == sizeof(entries)/sizeof(entries[0])) + break; + } + cmd_rx_bw_start(lcore_id); + cmd_tx_bw_start(lcore_id); + if (n_entries == sizeof(entries)/sizeof(entries[0])) + break; + } + + struct header hdr; + + header_init(&hdr, rte_get_tsc_hz(), rte_rdtsc(), n_entries); + header_add_field(&hdr, sizeof(((struct record *)0)->lcore_id)); + header_add_field(&hdr, sizeof(((struct record *)0)->task_id)); + header_add_field(&hdr, sizeof(((struct record *)0)->rx_bytes)); + header_add_field(&hdr, sizeof(((struct record *)0)->tx_bytes)); + header_add_field(&hdr, sizeof(((struct record *)0)->drop_bytes)); + header_add_field(&hdr, sizeof(((struct record *)0)->tsc)); + header_write(&hdr, fp); +} + +void stats_cons_log_notify(void) +{ + for (uint32_t i = 0; i < n_entries; ++i) { + uint32_t c = entries[i].lcore_id; + uint32_t t = entries[i].task_id; + struct task_stats *l = stats_get_task_stats(c, t); + struct task_stats_sample *last = stats_get_task_stats_sample(c, t, 1); + + buf[buf_pos].lcore_id = c; + buf[buf_pos].task_id = t; + buf[buf_pos].tx_bytes = last->tx_bytes; + buf[buf_pos].rx_bytes = last->rx_bytes; + buf[buf_pos].drop_bytes = last->drop_bytes; + /* buf[buf_pos].drop_tx_fail = l->tot_drop_tx_fail; */ + buf[buf_pos].tsc = last->tsc; + + buf_pos++; + + if (buf_pos == sizeof(buf)/sizeof(buf[0])) { + fwrite(buf, sizeof(buf), 1, fp); + buf_pos = 0; + } + } +} +#endif + +void stats_cons_log_finish(void) +{ + if (fp) { + if (buf_pos) { + fwrite(buf, sizeof(buf[0]), buf_pos, fp); + buf_pos = 0; + } + fclose(fp); + } +} diff --git a/VNFs/DPPD-PROX/stats_cons_log.h b/VNFs/DPPD-PROX/stats_cons_log.h new file mode 100644 index 00000000..92597de2 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_cons_log.h @@ -0,0 +1,28 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_CONS_LOG_H_ +#define _STATS_CONS_LOG_H_ + +#include "stats_cons.h" + +void stats_cons_log_init(void); +void stats_cons_log_notify(void); +void stats_cons_log_finish(void); + +struct stats_cons *stats_cons_log_get(void); + +#endif /* _STATS_CONS_LOG_H_ */ diff --git a/VNFs/DPPD-PROX/stats_core.c b/VNFs/DPPD-PROX/stats_core.c new file mode 100644 index 00000000..845399e3 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_core.c @@ -0,0 +1,293 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_lcore.h> + +#include "prox_malloc.h" +#include "stats_core.h" +#include "cqm.h" +#include "log.h" +#include "msr.h" +#include "parse_utils.h" +#include "prox_cfg.h" +#include "lconf.h" + +struct stats_core_manager { + struct rdt_features rdt_features; + int msr_support; + int max_core_id; + uint16_t n_lcore_stats; + int cache_size[RTE_MAX_LCORE]; + struct lcore_stats lcore_stats_set[0]; +}; + +static struct stats_core_manager *scm; +extern int last_stat; + +static int get_L3_size(void) +{ + char buf[1024]= "/proc/cpuinfo"; + FILE* fd = fopen(buf, "r"); + if (fd == NULL) { + plogx_err("Could not open %s", buf); + return -1; + } + int lcore = -1, val = 0, size = 0; + while (fgets(buf, sizeof(buf), fd) != NULL) { + if (sscanf(buf, "processor : %u", &val) == 1) { + lcore = val; + scm->max_core_id = lcore; + } + if (sscanf(buf, "cache size : %u", &val) == 1) { + size = val; + if ((lcore != -1) && (lcore < RTE_MAX_LCORE)) { + scm->cache_size[lcore] = size * 1024; + } + } + } + fclose(fd); + plog_info("\tMaximum core_id = %d\n", scm->max_core_id); + return 0; +} + +int stats_get_n_lcore_stats(void) +{ + return scm->n_lcore_stats; +} + +int stats_cpu_freq_enabled(void) +{ + return scm->msr_support; +} + +int stats_cmt_enabled(void) +{ + return cmt_is_supported(); +} + +int stats_cat_enabled(void) +{ + return cat_is_supported(); +} + +int stats_mbm_enabled(void) +{ + return mbm_is_supported(); +} + +uint32_t stats_lcore_find_stat_id(uint32_t lcore_id) +{ + for (int i = 0; i < scm->n_lcore_stats; ++i) + if (scm->lcore_stats_set[i].lcore_id == lcore_id) + return i; + return 0; +} + +struct lcore_stats_sample *stats_get_lcore_stats_sample(uint32_t stat_id, int l) +{ + return &scm->lcore_stats_set[stat_id].sample[l == last_stat]; +} + +struct lcore_stats *stats_get_lcore_stats(uint32_t stat_id) +{ + return &scm->lcore_stats_set[stat_id]; +} + +static struct stats_core_manager *alloc_stats_core_manager(void) +{ + const int socket_id = rte_lcore_to_socket_id(rte_lcore_id()); + uint32_t n_lcore_stats = 0; + uint32_t lcore_id; + size_t mem_size; + + lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) + n_lcore_stats++; + mem_size = sizeof(struct stats_core_manager) + sizeof(struct lcore_stats) * n_lcore_stats; + return prox_zmalloc(mem_size, socket_id); +} + +void stats_lcore_init(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id; + int j = 0; + + scm = alloc_stats_core_manager(); + + if (is_virtualized()) { + plog_info("Not initializing msr as running in a VM\n"); + scm->msr_support = 0; + } else if ((scm->msr_support = !msr_init()) == 0) { + plog_warn("Failed to open msr pseudo-file (missing msr kernel module?)\n"); + } + + scm->n_lcore_stats = 0; + lcore_id = -1; + get_L3_size(); + while (prox_core_next(&lcore_id, 0) == 0) { + scm->lcore_stats_set[scm->n_lcore_stats++].lcore_id = lcore_id; + } + if (!rdt_is_supported()) + return; + + if (!scm->msr_support) { + plog_warn("CPU supports RDT but msr module not loaded. Disabling RDT stats.\n"); + return; + } + + if (0 != rdt_get_features(&scm->rdt_features)) { + plog_warn("Failed to get RDT features\n"); + return; + } + else { + rdt_init_stat_core(rte_lcore_id()); + } + + /* Start using last rmid, to keep first rmid for technologies (like cat) where there are less rmid */ + uint32_t last_rmid = scm->rdt_features.cmt_max_rmid; + for (uint32_t i = 0; i < scm->n_lcore_stats; ++i) { + scm->lcore_stats_set[i].rmid = last_rmid; // cmt_max_rmid is used by non-monitored cores + last_rmid--; + } + + uint64_t cache_set; + for (uint32_t i = 0; i < scm->n_lcore_stats; ++i) { + plog_info("\tAssociating core %u to rmid %lu (associating each core used by prox to a different rmid)\n", scm->lcore_stats_set[i].lcore_id, scm->lcore_stats_set[i].rmid); + cqm_assoc(scm->lcore_stats_set[i].lcore_id, scm->lcore_stats_set[i].rmid); + uint32_t lcore_id = scm->lcore_stats_set[i].lcore_id; + lconf = &lcore_cfg[lcore_id]; + cache_set = lconf->cache_set; + if ((cache_set) && (cache_set < PROX_MAX_CACHE_SET)) { + scm->lcore_stats_set[i].class = cache_set; + scm->lcore_stats_set[i].cat_mask = prox_cache_set_cfg[cache_set].mask; + if (prox_cache_set_cfg[cache_set].socket_id == -1) { + prox_cache_set_cfg[cache_set].socket_id = scm->lcore_stats_set[i].socket_id; + prox_cache_set_cfg[cache_set].lcore_id = lcore_id; + } else if (prox_cache_set_cfg[cache_set].socket_id != (int32_t)scm->lcore_stats_set[i].socket_id) { + plog_err("Unsupported config: Using same cache set on two different socket\n"); + } + } else { + scm->lcore_stats_set[i].class = 0; + scm->lcore_stats_set[i].cat_mask = (1 << cat_get_num_ways()) -1; + } + } + cat_log_init(0); + last_rmid = scm->rdt_features.cat_max_rmid; + for (int i = 0; i < PROX_MAX_CACHE_SET; i++) { + if (prox_cache_set_cfg[i].mask) { + plog_info("\tSetting cache set %d to %x\n", i, prox_cache_set_cfg[i].mask); + cat_set_class_mask(prox_cache_set_cfg[i].lcore_id, i, prox_cache_set_cfg[i].mask); + } + } + for (uint32_t i = 0; i < scm->n_lcore_stats; ++i) { + uint32_t lcore_id = scm->lcore_stats_set[i].lcore_id; + lconf = &lcore_cfg[lcore_id]; + cache_set = lconf->cache_set; + if (cache_set) { + if (prox_cache_set_cfg[cache_set].mask) { + scm->lcore_stats_set[i].rmid = (scm->lcore_stats_set[i].rmid) | (cache_set << 32); + plog_info("\tCache set = %ld for core %d\n", cache_set, lcore_id); + cqm_assoc(lcore_id, scm->lcore_stats_set[i].rmid); + } else { + plog_err("\tUndefined Cache set = %ld for core %d\n", cache_set, lcore_id); + } + } else { + if (prox_cache_set_cfg[cache_set].mask) { + scm->lcore_stats_set[i].rmid = (scm->lcore_stats_set[i].rmid); + plog_info("\tUsing default cache set for core %d\n", lcore_id); + cqm_assoc(lcore_id, scm->lcore_stats_set[i].rmid); + } else { + plog_info("\tNo default cache set for core %d\n", lcore_id); + } + } + } +} + +static void stats_lcore_update_freq(void) +{ + for (uint8_t i = 0; i < scm->n_lcore_stats; ++i) { + struct lcore_stats *ls = &scm->lcore_stats_set[i]; + struct lcore_stats_sample *lss = &ls->sample[last_stat]; + + msr_read(&lss->afreq, ls->lcore_id, 0xe8); + msr_read(&lss->mfreq, ls->lcore_id, 0xe7); + } +} +void stats_update_cache_mask(uint32_t lcore_id, uint32_t mask) +{ + for (uint8_t i = 0; i < scm->n_lcore_stats; ++i) { + struct lcore_stats *ls = &scm->lcore_stats_set[i]; + if (ls->lcore_id == lcore_id) { + plog_info("Updating core %d stats %d to mask %x\n", lcore_id, i, mask); + scm->lcore_stats_set[i].cat_mask = mask; + } + } +} + +static void stats_lcore_update_rdt(void) +{ + for (uint8_t i = 0; i < scm->n_lcore_stats; ++i) { + struct lcore_stats *ls = &scm->lcore_stats_set[i]; + + if (ls->rmid) { + cmt_read_ctr(&ls->cmt_data, ls->rmid, ls->lcore_id); + mbm_read_tot_bdw(&ls->mbm_tot, ls->rmid, ls->lcore_id); + mbm_read_loc_bdw(&ls->mbm_loc, ls->rmid, ls->lcore_id); + } + } +} + +void stats_lcore_post_proc(void) +{ + /* update CQM stats (calculate fraction and bytes reported) */ + for (uint8_t i = 0; i < scm->n_lcore_stats; ++i) { + struct lcore_stats *ls = &scm->lcore_stats_set[i]; + struct lcore_stats_sample *lss = &ls->sample[last_stat]; + + if (ls->rmid) { + ls->cmt_bytes = ls->cmt_data * scm->rdt_features.upscaling_factor; + lss->mbm_tot_bytes = ls->mbm_tot * scm->rdt_features.upscaling_factor; + lss->mbm_loc_bytes = ls->mbm_loc * scm->rdt_features.upscaling_factor; + plogx_dbg("cache[core %d] = %ld\n", ls->lcore_id, ls->cmt_bytes); + } + } + for (uint8_t i = 0; i < scm->n_lcore_stats; ++i) { + struct lcore_stats *ls = &scm->lcore_stats_set[i]; + + if (ls->rmid && scm->cache_size[ls->lcore_id]) + ls->cmt_fraction = ls->cmt_bytes * 10000 / scm->cache_size[ls->lcore_id]; + else + ls->cmt_fraction = 0; + } +} + +void stats_lcore_update(void) +{ + if (scm->msr_support) + stats_lcore_update_freq(); + if (rdt_is_supported()) + stats_lcore_update_rdt(); +} + +void stats_lcore_assoc_rmid(void) +{ + for (uint32_t i = 0; i < scm->n_lcore_stats; ++i) { + uint32_t lcore_id = scm->lcore_stats_set[i].lcore_id; + scm->lcore_stats_set[i].rmid = scm->lcore_stats_set[i].rmid & 0xffffffff; + cqm_assoc(lcore_id, scm->lcore_stats_set[i].rmid); + } +} diff --git a/VNFs/DPPD-PROX/stats_core.h b/VNFs/DPPD-PROX/stats_core.h new file mode 100644 index 00000000..f9939def --- /dev/null +++ b/VNFs/DPPD-PROX/stats_core.h @@ -0,0 +1,59 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_CORE_H_ +#define _STATS_CORE_H_ + +#include <inttypes.h> + +struct lcore_stats_sample { + uint64_t afreq; + uint64_t mfreq; + uint64_t mbm_tot_bytes; + uint64_t mbm_loc_bytes; +}; + +struct lcore_stats { + uint32_t lcore_id; + uint32_t socket_id; + uint64_t rmid; + uint64_t cmt_data; + uint64_t cmt_bytes; + uint64_t mbm_tot_bytes; + uint64_t mbm_loc_bytes; + uint64_t cmt_fraction; + uint32_t cat_mask; + uint64_t mbm_tot; + uint64_t mbm_loc; + uint32_t class; + struct lcore_stats_sample sample[2]; +}; + +uint32_t stats_lcore_find_stat_id(uint32_t lcore_id); +int stats_get_n_lcore_stats(void); +struct lcore_stats *stats_get_lcore_stats(uint32_t stat_id); +struct lcore_stats_sample *stats_get_lcore_stats_sample(uint32_t stat_id, int last); +int stats_cpu_freq_enabled(void); +int stats_cmt_enabled(void); +int stats_cat_enabled(void); +int stats_mbm_enabled(void); +void stats_lcore_update(void); +void stats_lcore_init(void); +void stats_lcore_post_proc(void); +void stats_update_cache_mask(uint32_t lcore_id, uint32_t mask); +void stats_lcore_assoc_rmid(void); + +#endif /* _STATS_CORE_H_ */ diff --git a/VNFs/DPPD-PROX/stats_global.c b/VNFs/DPPD-PROX/stats_global.c new file mode 100644 index 00000000..d7529d34 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_global.c @@ -0,0 +1,110 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <rte_cycles.h> +#include <inttypes.h> + +#include "stats_global.h" +#include "stats_port.h" +#include "stats_task.h" + +struct global_stats { + struct global_stats_sample sample[2]; + struct global_stats_sample beg; + uint8_t started_avg; + uint64_t start_tsc; + uint64_t end_tsc; +}; + +extern int last_stat; +static struct global_stats global_stats; + +uint64_t stats_get_last_tsc(void) +{ + return global_stats.sample[last_stat].tsc; +} + +uint64_t stats_global_start_tsc(void) +{ + return global_stats.start_tsc; +} + +uint64_t stats_global_beg_tsc(void) +{ + return global_stats.beg.tsc; +} + +uint64_t stats_global_end_tsc(void) +{ + return global_stats.end_tsc; +} + +struct global_stats_sample *stats_get_global_stats(int last) +{ + return &global_stats.sample[last == last_stat]; +} + +struct global_stats_sample *stats_get_global_stats_beg(void) +{ + return (global_stats.beg.tsc < global_stats.sample[last_stat].tsc)? &global_stats.beg : NULL; +} + +void stats_global_reset(void) +{ + uint64_t now = rte_rdtsc(); + uint64_t last_tsc = global_stats.sample[last_stat].tsc; + uint64_t prev_tsc = global_stats.sample[!last_stat].tsc; + uint64_t end_tsc = global_stats.end_tsc; + + memset(&global_stats, 0, sizeof(struct global_stats)); + global_stats.sample[last_stat].tsc = last_tsc; + global_stats.sample[!last_stat].tsc = prev_tsc; + global_stats.start_tsc = now; + global_stats.beg.tsc = now; + global_stats.end_tsc = end_tsc; +} + +void stats_global_init(unsigned avg_start, unsigned duration) +{ + uint64_t now = rte_rdtsc(); + + global_stats.start_tsc = now; + /* + 1 for rounding */ + tsc_hz = rte_get_tsc_hz(); + if (duration) + global_stats.end_tsc = global_stats.start_tsc + (avg_start + duration + 1) * tsc_hz; + + global_stats.beg.tsc = global_stats.start_tsc + avg_start * tsc_hz; +} + +void stats_global_post_proc(void) +{ + uint64_t *rx = &global_stats.sample[last_stat].host_rx_packets; + uint64_t *tx = &global_stats.sample[last_stat].host_tx_packets; + uint64_t *tsc = &global_stats.sample[last_stat].tsc; + + stats_task_get_host_rx_tx_packets(rx, tx, tsc); + global_stats.sample[last_stat].nics_ierrors = stats_port_get_ierrors(); + global_stats.sample[last_stat].nics_imissed = stats_port_get_imissed(); + global_stats.sample[last_stat].nics_rx_packets = stats_port_get_rx_packets(); + global_stats.sample[last_stat].nics_tx_packets = stats_port_get_tx_packets(); + + if (global_stats.sample[last_stat].tsc > global_stats.beg.tsc && !global_stats.started_avg) { + global_stats.started_avg = 1; + global_stats.beg = global_stats.sample[last_stat]; + } +} diff --git a/VNFs/DPPD-PROX/stats_global.h b/VNFs/DPPD-PROX/stats_global.h new file mode 100644 index 00000000..8f53ab5c --- /dev/null +++ b/VNFs/DPPD-PROX/stats_global.h @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_GLOBAL_H_ +#define _STATS_GLOBAL_H_ + +uint64_t stats_get_last_tsc(void); + +struct global_stats_sample { + uint64_t tsc; + uint64_t host_rx_packets; + uint64_t host_tx_packets; + uint64_t nics_rx_packets; + uint64_t nics_tx_packets; + uint64_t nics_ierrors; + uint64_t nics_imissed; +}; + +void stats_global_reset(void); +void stats_global_init(unsigned avg_start, unsigned duration); +void stats_global_post_proc(void); + +struct global_stats_sample *stats_get_global_stats(int last); +struct global_stats_sample *stats_get_global_stats_beg(void); +uint64_t stats_global_start_tsc(void); +uint64_t stats_global_beg_tsc(void); +uint64_t stats_global_end_tsc(void); + +#endif /* _STATS_GLOBAL_H_ */ diff --git a/VNFs/DPPD-PROX/stats_l4gen.c b/VNFs/DPPD-PROX/stats_l4gen.c new file mode 100644 index 00000000..37af7ac4 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_l4gen.c @@ -0,0 +1,110 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> + +#include "prox_malloc.h" +#include "prox_cfg.h" +#include "stats_l4gen.h" +#include "task_init.h" + +struct task_l4gen_stats { + struct task_base base; + struct l4_stats l4_stats; +}; + +struct stats_l4gen_manager { + uint16_t n_l4gen; + struct task_l4_stats task_l4_stats[0]; +}; + +extern int last_stat; +static struct stats_l4gen_manager *sl4m; + +int stats_get_n_l4gen(void) +{ + return sl4m->n_l4gen; +} + +struct task_l4_stats *stats_get_l4_stats(uint32_t i) +{ + return &sl4m->task_l4_stats[i]; +} + +struct l4_stats_sample *stats_get_l4_stats_sample(uint32_t i, int l) +{ + return &sl4m->task_l4_stats[i].sample[l == last_stat]; +} + +static struct stats_l4gen_manager *alloc_stats_l4gen_manager(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + size_t mem_size; + uint32_t n_l4gen = 0; + const int socket_id = rte_lcore_to_socket_id(rte_lcore_id()); + + lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + + if (!strcmp(targ->task_init->mode_str, "genl4")) + n_l4gen++; + } + } + + mem_size = sizeof(struct stats_l4gen_manager) + sizeof(struct task_l4_stats) * n_l4gen; + return prox_zmalloc(mem_size, socket_id); +} + +void stats_l4gen_init(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + + sl4m = alloc_stats_l4gen_manager(); + + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + + if (!strcmp(targ->task_init->mode_str, "genl4")) { + sl4m->task_l4_stats[sl4m->n_l4gen].task = (struct task_l4gen_stats *)lconf->tasks_all[task_id]; + sl4m->task_l4_stats[sl4m->n_l4gen].lcore_id = lcore_id; + sl4m->task_l4_stats[sl4m->n_l4gen].task_id = task_id; + sl4m->n_l4gen++; + } + } + } +} + +void stats_l4gen_update(void) +{ + uint64_t before, after; + + for (uint16_t i = 0; i < sl4m->n_l4gen; ++i) { + struct task_l4gen_stats *task_l4gen = sl4m->task_l4_stats[i].task; + + before = rte_rdtsc(); + sl4m->task_l4_stats[i].sample[last_stat].stats = task_l4gen->l4_stats; + after = rte_rdtsc(); + + sl4m->task_l4_stats[i].sample[last_stat].tsc = (before >> 1) + (after >> 1); + } +} diff --git a/VNFs/DPPD-PROX/stats_l4gen.h b/VNFs/DPPD-PROX/stats_l4gen.h new file mode 100644 index 00000000..dfc4e111 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_l4gen.h @@ -0,0 +1,44 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_L4GEN_H_ +#define _STATS_L4GEN_H_ + +#include <inttypes.h> + +#include "genl4_bundle.h" + +struct task_l4gen_stats; + +struct l4_stats_sample { + uint64_t tsc; + struct l4_stats stats; +}; + +struct task_l4_stats { + struct task_l4gen_stats *task; + struct l4_stats_sample sample[2]; + uint8_t lcore_id; + uint8_t task_id; +}; + +void stats_l4gen_init(void); +void stats_l4gen_update(void); +int stats_get_n_l4gen(void); +struct task_l4_stats *stats_get_l4_stats(uint32_t i); +struct l4_stats_sample *stats_get_l4_stats_sample(uint32_t i, int l); + +#endif /* _STATS_L4GEN_H_ */ diff --git a/VNFs/DPPD-PROX/stats_latency.c b/VNFs/DPPD-PROX/stats_latency.c new file mode 100644 index 00000000..52027892 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_latency.c @@ -0,0 +1,225 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "prox_malloc.h" +#include "stats_latency.h" +#include "handle_lat.h" +#include "prox_cfg.h" +#include "prox_args.h" + +struct stats_latency_manager_entry { + struct task_lat *task; + uint8_t lcore_id; + uint8_t task_id; + struct lat_test lat_test; + struct lat_test tot_lat_test; + struct stats_latency stats; + struct stats_latency tot; +}; + +struct stats_latency_manager { + uint16_t n_latency; + struct stats_latency_manager_entry entries[0]; /* copy of stats when running update stats. */ +}; + +static struct stats_latency_manager *slm; + +void stats_latency_reset(void) +{ + for (uint16_t i = 0; i < slm->n_latency; ++i) + lat_test_reset(&slm->entries[i].tot_lat_test); +} + +int stats_get_n_latency(void) +{ + return slm->n_latency; +} + +uint32_t stats_latency_get_core_id(uint32_t i) +{ + return slm->entries[i].lcore_id; +} + +uint32_t stats_latency_get_task_id(uint32_t i) +{ + return slm->entries[i].task_id; +} + +struct stats_latency *stats_latency_get(uint32_t i) +{ + return &slm->entries[i].stats; +} + +struct stats_latency *stats_latency_tot_get(uint32_t i) +{ + return &slm->entries[i].tot; +} + +static struct stats_latency_manager_entry *stats_latency_entry_find(uint8_t lcore_id, uint8_t task_id) +{ + struct stats_latency_manager_entry *entry; + + for (uint16_t i = 0; i < stats_get_n_latency(); ++i) { + entry = &slm->entries[i]; + + if (entry->lcore_id == lcore_id && entry->task_id == task_id) { + return entry; + } + } + return NULL; +} + +struct stats_latency *stats_latency_tot_find(uint32_t lcore_id, uint32_t task_id) +{ + struct stats_latency_manager_entry *entry = stats_latency_entry_find(lcore_id, task_id); + + if (!entry) + return NULL; + else + return &entry->tot; +} + +struct stats_latency *stats_latency_find(uint32_t lcore_id, uint32_t task_id) +{ + struct stats_latency_manager_entry *entry = stats_latency_entry_find(lcore_id, task_id); + + if (!entry) + return NULL; + else + return &entry->stats; +} + +static int task_runs_observable_latency(struct task_args *targ) +{ + /* TODO: make this work with multiple ports and with + rings. Currently, only showing lat tasks which have 1 RX + port. */ + return !strcmp(targ->task_init->mode_str, "lat") && + (targ->nb_rxports == 1 || targ->nb_rxrings == 1); +} + +static struct stats_latency_manager *alloc_stats_latency_manager(void) +{ + const uint32_t socket_id = rte_lcore_to_socket_id(rte_lcore_id()); + struct stats_latency_manager *ret; + struct lcore_cfg *lconf; + uint32_t n_latency = 0; + uint32_t lcore_id; + size_t mem_size; + + lcore_id = -1; + while (prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + if (task_runs_observable_latency(targ)) + ++n_latency; + } + } + mem_size = sizeof(*ret) + sizeof(ret->entries[0]) * n_latency; + + ret = prox_zmalloc(mem_size, socket_id); + return ret; +} + +static void stats_latency_add_task(struct lcore_cfg *lconf, struct task_args *targ) +{ + struct stats_latency_manager_entry *new_entry = &slm->entries[slm->n_latency]; + + new_entry->task = (struct task_lat *)targ->tbase; + new_entry->lcore_id = lconf->id; + new_entry->task_id = targ->id; + slm->n_latency++; +} + +void stats_latency_init(void) +{ + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + + slm = alloc_stats_latency_manager(); + + while (core_targ_next(&lconf, &targ, 0) == 0) { + if (task_runs_observable_latency(targ)) + stats_latency_add_task(lconf, targ); + } +} + +#ifdef LATENCY_HISTOGRAM +void stats_core_lat_histogram(uint8_t lcore_id, uint8_t task_id, uint64_t **buckets) +{ + struct stats_latency_manager_entry *lat_stats; + uint64_t tsc; + + lat_stats = stats_latency_entry_find(lcore_id, task_id); + + if (lat_stats) + *buckets = lat_stats->lat_test.buckets; + else + *buckets = NULL; +} +#endif + +static void stats_latency_fetch_entry(struct stats_latency_manager_entry *entry) +{ + struct stats_latency *cur = &entry->stats; + struct lat_test *lat_test_local = &entry->lat_test; + struct lat_test *lat_test_remote = task_lat_get_latency_meassurement(entry->task); + + if (!lat_test_remote) + return; + + if (lat_test_remote->tot_all_pkts) { + lat_test_copy(&entry->lat_test, lat_test_remote); + lat_test_reset(lat_test_remote); + lat_test_combine(&entry->tot_lat_test, &entry->lat_test); + } + + task_lat_use_other_latency_meassurement(entry->task); +} + +static void stats_latency_from_lat_test(struct stats_latency *dst, struct lat_test *src) +{ + /* In case packets were received, but measurements were too + inaccurate */ + if (src->tot_pkts) { + dst->max = lat_test_get_max(src); + dst->min = lat_test_get_min(src); + dst->avg = lat_test_get_avg(src); + dst->stddev = lat_test_get_stddev(src); + } + dst->accuracy_limit = lat_test_get_accuracy_limit(src); + dst->tot_packets = src->tot_pkts; + dst->tot_all_packets = src->tot_all_pkts; + dst->lost_packets = src->lost_packets; +} + +static void stats_latency_update_entry(struct stats_latency_manager_entry *entry) +{ + if (!entry->lat_test.tot_all_pkts) + return; + + stats_latency_from_lat_test(&entry->stats, &entry->lat_test); + stats_latency_from_lat_test(&entry->tot, &entry->tot_lat_test); +} + +void stats_latency_update(void) +{ + for (uint16_t i = 0; i < slm->n_latency; ++i) + stats_latency_fetch_entry(&slm->entries[i]); + for (uint16_t i = 0; i < slm->n_latency; ++i) + stats_latency_update_entry(&slm->entries[i]); +} diff --git a/VNFs/DPPD-PROX/stats_latency.h b/VNFs/DPPD-PROX/stats_latency.h new file mode 100644 index 00000000..83cd4a18 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_latency.h @@ -0,0 +1,54 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_LATENCY_H_ +#define _STATS_LATENCY_H_ + +#include <inttypes.h> + +#include "handle_lat.h" + +struct stats_latency { + struct time_unit_err avg; + struct time_unit_err min; + struct time_unit_err max; + struct time_unit_err stddev; + + struct time_unit accuracy_limit; + uint64_t lost_packets; + uint64_t tot_packets; + uint64_t tot_all_packets; +}; + +uint32_t stats_latency_get_core_id(uint32_t i); +uint32_t stats_latency_get_task_id(uint32_t i); +struct stats_latency *stats_latency_get(uint32_t i); +struct stats_latency *stats_latency_find(uint32_t lcore_id, uint32_t task_id); + +struct stats_latency *stats_latency_tot_get(uint32_t i); +struct stats_latency *stats_latency_tot_find(uint32_t lcore_id, uint32_t task_id); + +void stats_latency_init(void); +void stats_latency_update(void); +void stats_latency_reset(void); + +int stats_get_n_latency(void); + +#ifdef LATENCY_HISTOGRAM +void stats_core_lat_histogram(uint8_t lcore_id, uint8_t task_id, uint64_t **buckets); +#endif + +#endif /* _STATS_LATENCY_H_ */ diff --git a/VNFs/DPPD-PROX/stats_mempool.c b/VNFs/DPPD-PROX/stats_mempool.c new file mode 100644 index 00000000..c5861eb5 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_mempool.c @@ -0,0 +1,96 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_mempool.h> +#include <rte_version.h> +#include <inttypes.h> + +#include "prox_malloc.h" +#include "prox_port_cfg.h" +#include "stats_mempool.h" + +struct stats_mempool_manager { + uint32_t n_mempools; + struct mempool_stats mempool_stats[0]; +}; + +static struct stats_mempool_manager *smm; + +struct mempool_stats *stats_get_mempool_stats(uint32_t i) +{ + return &smm->mempool_stats[i]; +} + +int stats_get_n_mempools(void) +{ + return smm->n_mempools; +} + +static struct stats_mempool_manager *alloc_stats_mempool_manager(void) +{ + const uint32_t socket_id = rte_lcore_to_socket_id(rte_lcore_id()); + uint32_t n_max_mempools = sizeof(prox_port_cfg[0].pool)/sizeof(prox_port_cfg[0].pool[0]); + uint32_t n_mempools = 0; + size_t mem_size = sizeof(struct stats_mempool_manager); + + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + if (!prox_port_cfg[i].active) + continue; + + for (uint8_t j = 0; j < n_max_mempools; ++j) { + if (prox_port_cfg[i].pool[j] && prox_port_cfg[i].pool_size[j]) { + mem_size += sizeof(struct mempool_stats); + } + } + } + + return prox_zmalloc(mem_size, socket_id); +} + +void stats_mempool_init(void) +{ + uint32_t n_max_mempools = sizeof(prox_port_cfg[0].pool)/sizeof(prox_port_cfg[0].pool[0]); + + smm = alloc_stats_mempool_manager(); + for (uint8_t i = 0; i < PROX_MAX_PORTS; ++i) { + if (!prox_port_cfg[i].active) + continue; + + for (uint8_t j = 0; j < n_max_mempools; ++j) { + if (prox_port_cfg[i].pool[j] && prox_port_cfg[i].pool_size[j]) { + struct mempool_stats *ms = &smm->mempool_stats[smm->n_mempools]; + + ms->pool = prox_port_cfg[i].pool[j]; + ms->port = i; + ms->queue = j; + ms->size = prox_port_cfg[i].pool_size[j]; + smm->n_mempools++; + } + } + } +} + +void stats_mempool_update(void) +{ + for (uint8_t mp_id = 0; mp_id < smm->n_mempools; ++mp_id) { + /* Note: The function free_count returns the number of used entries. */ +#if RTE_VERSION >= RTE_VERSION_NUM(17,5,0,0) + smm->mempool_stats[mp_id].free = rte_mempool_avail_count(smm->mempool_stats[mp_id].pool); +#else + smm->mempool_stats[mp_id].free = rte_mempool_count(smm->mempool_stats[mp_id].pool); +#endif + } +} diff --git a/VNFs/DPPD-PROX/stats_mempool.h b/VNFs/DPPD-PROX/stats_mempool.h new file mode 100644 index 00000000..b62e111f --- /dev/null +++ b/VNFs/DPPD-PROX/stats_mempool.h @@ -0,0 +1,36 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_MEMPOOL_H_ +#define _STATS_MEMPOOL_H_ + +#include <inttypes.h> +#include <stddef.h> + +struct mempool_stats { + struct rte_mempool *pool; + uint16_t port; + uint16_t queue; + size_t free; + size_t size; +}; + +void stats_mempool_init(void); +struct mempool_stats *stats_get_mempool_stats(uint32_t i); +int stats_get_n_mempools(void); +void stats_mempool_update(void); + +#endif /* _STATS_MEMPOOL_H_ */ diff --git a/VNFs/DPPD-PROX/stats_parser.c b/VNFs/DPPD-PROX/stats_parser.c new file mode 100644 index 00000000..aa9d6741 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_parser.c @@ -0,0 +1,897 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stddef.h> + +#include "stats_parser.h" +#include "log.h" +#include "stats.h" +#include "parse_utils.h" +#include "handle_lat.h" +#include "prox_port_cfg.h" +#include "stats_port.h" +#include "stats_mempool.h" +#include "stats_ring.h" +#include "stats_l4gen.h" +#include "stats_latency.h" +#include "stats_global.h" +#include "stats_prio_task.h" + +struct stats_path_str { + const char *str; + uint64_t (*func)(int argc, const char *argv[]); +}; + +static int args_to_core_task(const char *core_str, const char *task_str, uint32_t *lcore_id, uint32_t *task_id) +{ + if (parse_list_set(lcore_id, core_str, 1) != 1) + return -1; + *task_id = atoi(task_str); + + return 0; +} + +static uint64_t sp_task_idle_cycles(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->tsc; +} + +static uint64_t sp_task_rx_packets(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->rx_pkt_count; +} + +static uint64_t sp_task_tx_packets(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->tx_pkt_count; +} + +static uint64_t sp_task_drop_tx_fail(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->drop_tx_fail; +} + +static uint64_t sp_task_drop_tx_fail_prio(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + if (stats_get_prio_task_stats_sample_by_core_task(c, t, 1)) + return stats_get_prio_task_stats_sample_by_core_task(c, t, 1)->drop_tx_fail_prio[atoi(argv[2])]; + else + return -1; +} + +static uint64_t sp_task_rx_prio(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_prio_task_stats_sample_by_core_task(c, t, 1)->rx_prio[atoi(argv[2])]; +} + +static uint64_t sp_task_drop_discard(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->drop_discard; +} + +static uint64_t sp_task_drop_handled(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->drop_handled; +} + +static uint64_t sp_task_rx_bytes(int argc, const char *argv[]) +{ + return -1; +} + +static uint64_t sp_task_tx_bytes(int argc, const char *argv[]) +{ + return -1; +} + +static uint64_t sp_task_tsc(int argc, const char *argv[]) +{ + struct task_stats_sample *last; + uint32_t c, t; + + if (args_to_core_task(argv[0], argv[1], &c, &t)) + return -1; + return stats_get_task_stats_sample(c, t, 1)->tsc; +} + +static uint64_t sp_l4gen_created(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_created + clast->stats.udp_created; +} + +static uint64_t sp_l4gen_finished(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_finished_retransmit + clast->stats.tcp_finished_no_retransmit + + clast->stats.udp_finished + clast->stats.udp_expired + clast->stats.tcp_expired; +} + +static uint64_t sp_l4gen_expire_tcp(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_expired; +} + +static uint64_t sp_l4gen_expire_udp(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.udp_expired; +} + +static uint64_t sp_l4gen_retx(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_retransmits; +} + +static uint64_t sp_l4gen_tsc(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->tsc; +} + +static uint64_t sp_l4gen_torndown_no_retx(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_finished_no_retransmit; +} + +static uint64_t sp_l4gen_torndown_retx(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_finished_retransmit; +} + +static uint64_t sp_l4gen_torndown_udp(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.udp_finished; +} + +static uint64_t sp_l4gen_created_tcp(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_created; + +} + +static uint64_t sp_l4gen_created_udp(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.udp_created; +} + +static uint64_t sp_l4gen_created_all(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.tcp_created + clast->stats.udp_created; +} + +static uint64_t sp_l4gen_created_bundles(int argc, const char *argv[]) +{ + struct l4_stats_sample *clast = NULL; + + if (atoi(argv[0]) >= stats_get_n_l4gen()) + return -1; + clast = stats_get_l4_stats_sample(atoi(argv[0]), 1); + return clast->stats.bundles_created; +} + +static uint64_t sp_latency_min(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->min.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_mem_used(int argc, const char *argv[]) +{ + struct mempool_stats *ms; + + if (atoi(argv[0]) > stats_get_n_mempools()) + return -1; + ms = stats_get_mempool_stats(atoi(argv[0])); + return ms->size - ms->free; +} + +static uint64_t sp_mem_free(int argc, const char *argv[]) +{ + struct mempool_stats *ms; + + if (atoi(argv[0]) > stats_get_n_mempools()) + return -1; + ms = stats_get_mempool_stats(atoi(argv[0])); + return ms->free; +} + +static uint64_t sp_mem_size(int argc, const char *argv[]) +{ + struct mempool_stats *ms; + + if (atoi(argv[0]) > stats_get_n_mempools()) + return -1; + ms = stats_get_mempool_stats(atoi(argv[0])); + return ms->size; +} + +static uint64_t sp_port_no_mbufs(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->no_mbufs; +} + +static uint64_t sp_port_ierrors(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->ierrors; +} + +static uint64_t sp_port_imissed(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->imissed; +} + +static uint64_t sp_port_oerrors(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->oerrors; +} + +static uint64_t sp_port_rx_packets(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->rx_tot; +} + +static uint64_t sp_port_tx_packets(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_tot; +} + +static uint64_t sp_port_rx_bytes(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->rx_bytes; +} + +static uint64_t sp_port_tx_bytes(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_bytes; +} + +static uint64_t sp_port_tx_packets_64(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_64]; +} + +static uint64_t sp_port_tx_packets_65_127(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_65]; +} + +static uint64_t sp_port_tx_packets_128_255(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_128]; +} + +static uint64_t sp_port_tx_packets_256_511(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_256]; +} + +static uint64_t sp_port_tx_packets_512_1023(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_512]; +} + +static uint64_t sp_port_tx_packets_1024_1522(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_1024]; +} + +static uint64_t sp_port_tx_packets_1523_max(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tx_pkt_size[PKT_SIZE_1522]; +} + +static uint64_t sp_port_tsc(int argc, const char *argv[]) +{ + uint32_t port_id = atoi(argv[0]); + struct port_stats_sample *ps; + + if (port_id > PROX_MAX_PORTS || !prox_port_cfg[port_id].active) + return -1; + ps = stats_get_port_stats_sample(port_id, 1); + return ps->tsc; +} + +static uint64_t sp_latency_max(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->max.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_latency_avg(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->avg.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_latency_lost(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + return lat_test->lost_packets; +} + +static uint64_t sp_latency_tot_lost(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + return lat_test->lost_packets; +} + +static uint64_t sp_latency_total(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_all_packets) + return -1; + + return lat_test->tot_all_packets; +} + +static uint64_t sp_latency_used(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_all_packets) + return -1; + + return lat_test->tot_packets; +} + +static uint64_t sp_latency_tot_total(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_all_packets) + return -1; + + return lat_test->tot_all_packets; +} + +static uint64_t sp_latency_tot_used(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_all_packets) + return -1; + + return lat_test->tot_packets; +} + +static uint64_t sp_latency_tot_min(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->min.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_latency_tot_max(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->max.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_latency_tot_avg(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_tot_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->avg.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_latency_stddev(int argc, const char *argv[]) +{ + struct stats_latency *lat_test = NULL; + + if (atoi(argv[0]) >= stats_get_n_latency()) + return -1; + lat_test = stats_latency_get(atoi(argv[0])); + + if (!lat_test->tot_packets) + return -1; + + struct time_unit tu = lat_test->stddev.time; + return time_unit_to_usec(&tu); +} + +static uint64_t sp_ring_used(int argc, const char *argv[]) +{ + struct ring_stats *rs = NULL; + + if (atoi(argv[0]) >= stats_get_n_rings()) + return -1; + rs = stats_get_ring_stats(atoi(argv[0])); + return rs->size - rs->free; +} + +static uint64_t sp_ring_free(int argc, const char *argv[]) +{ + struct ring_stats *rs = NULL; + + if (atoi(argv[0]) >= stats_get_n_rings()) + return -1; + rs = stats_get_ring_stats(atoi(argv[0])); + return rs->free; +} + +static uint64_t sp_ring_size(int argc, const char *argv[]) +{ + struct ring_stats *rs = NULL; + + if (atoi(argv[0]) >= stats_get_n_rings()) + return -1; + rs = stats_get_ring_stats(atoi(argv[0])); + return rs->size; +} + +static uint64_t sp_global_host_rx_packets(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->host_rx_packets; +} + +static uint64_t sp_global_host_tx_packets(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->host_tx_packets; +} + +static uint64_t sp_global_nics_rx_packets(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->nics_rx_packets; +} + +static uint64_t sp_global_nics_tx_packets(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->nics_tx_packets; +} + +static uint64_t sp_global_nics_ierrors(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->nics_ierrors; +} + +static uint64_t sp_global_nics_imissed(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->nics_imissed; +} + +static uint64_t sp_global_tsc(int argc, const char *argv[]) +{ + return stats_get_global_stats(1)->tsc; +} + +static uint64_t sp_hz(int argc, const char *argv[]) +{ + return rte_get_tsc_hz(); +} + +struct stats_path_str stats_paths[] = { + {"hz", sp_hz}, + + {"global.host.rx.packets", sp_global_host_rx_packets}, + {"global.host.tx.packets", sp_global_host_tx_packets}, + {"global.nics.rx.packets", sp_global_nics_rx_packets}, + {"global.nics.tx.packets", sp_global_nics_tx_packets}, + {"global.nics.ierrrors", sp_global_nics_ierrors}, + {"global.nics.imissed", sp_global_nics_imissed}, + {"global.tsc", sp_global_tsc}, + + {"task.core(#).task(#).idle_cycles", sp_task_idle_cycles}, + {"task.core(#).task(#).rx.packets", sp_task_rx_packets}, + {"task.core(#).task(#).tx.packets", sp_task_tx_packets}, + {"task.core(#).task(#).drop.tx_fail", sp_task_drop_tx_fail}, + {"task.core(#).task(#).drop.discard", sp_task_drop_discard}, + {"task.core(#).task(#).drop.handled", sp_task_drop_handled}, + {"task.core(#).task(#).rx.bytes", sp_task_rx_bytes}, + {"task.core(#).task(#).tx.bytes", sp_task_tx_bytes}, + {"task.core(#).task(#).tsc", sp_task_tsc}, + {"task.core(#).task(#).drop.tx_fail_prio(#)", sp_task_drop_tx_fail_prio}, + {"task.core(#).task(#).rx_prio(#)", sp_task_rx_prio}, + + {"port(#).no_mbufs", sp_port_no_mbufs}, + {"port(#).ierrors", sp_port_ierrors}, + {"port(#).imissed", sp_port_imissed}, + {"port(#).oerrors", sp_port_oerrors}, + {"port(#).rx.packets", sp_port_rx_packets}, + {"port(#).tx.packets", sp_port_tx_packets}, + {"port(#).rx.bytes", sp_port_rx_bytes}, + {"port(#).tx.bytes", sp_port_tx_bytes}, + {"port(#).tx.packets_64", sp_port_tx_packets_64}, + {"port(#).tx.packets_65_127", sp_port_tx_packets_65_127}, + {"port(#).tx.packets_128_255", sp_port_tx_packets_128_255}, + {"port(#).tx.packets_256_511", sp_port_tx_packets_256_511}, + {"port(#).tx.packets_512_1023", sp_port_tx_packets_512_1023}, + {"port(#).tx.packets_1024_1522", sp_port_tx_packets_1024_1522}, + {"port(#).tx.packets_1523_max", sp_port_tx_packets_1523_max}, + {"port(#).tsc", sp_port_tsc}, + + {"mem(#).used", sp_mem_used}, + {"mem(#).free", sp_mem_free}, + {"mem(#).size", sp_mem_size}, + + {"latency(#).min", sp_latency_min}, + {"latency(#).max", sp_latency_max}, + {"latency(#).avg", sp_latency_avg}, + {"latency(#).lost", sp_latency_lost}, + {"latency(#).used", sp_latency_used}, + {"latency(#).total", sp_latency_total}, + {"latency(#).tot.min", sp_latency_tot_min}, + {"latency(#).tot.max", sp_latency_tot_max}, + {"latency(#).tot.avg", sp_latency_tot_avg}, + {"latency(#).tot.lost", sp_latency_tot_lost}, + {"latency(#).tot.used", sp_latency_tot_used}, + {"latency(#).tot.total", sp_latency_tot_total}, + {"latency(#).stddev", sp_latency_stddev}, + + {"ring(#).used", sp_ring_used}, + {"ring(#).free", sp_ring_free}, + {"ring(#).size", sp_ring_size}, + + {"l4gen(#).created.tcp", sp_l4gen_created_tcp}, + {"l4gen(#).created.udp", sp_l4gen_created_udp}, + {"l4gen(#).created.all", sp_l4gen_created_all}, + {"l4gen(#).created.bundles", sp_l4gen_created_bundles}, + {"l4gen(#).torndown.no_retx", sp_l4gen_torndown_no_retx}, + {"l4gen(#).torndown.retx", sp_l4gen_torndown_retx}, + {"l4gen(#).torndown.udp", sp_l4gen_torndown_udp}, + {"l4gen(#).expired.tcp", sp_l4gen_expire_tcp}, + {"l4gen(#).expired.udp", sp_l4gen_expire_udp}, + {"l4gen(#).created", sp_l4gen_created}, + {"l4gen(#).finished", sp_l4gen_finished}, + {"l4gen(#).retx", sp_l4gen_retx}, + {"l4gen(#).tsc", sp_l4gen_tsc}, +}; + +static int stats_parser_extract_args(char *stats_path, size_t *argc, char **argv) +{ + size_t len = strlen(stats_path); + size_t j = 0; + size_t k = 0; + int state = 0; + + for (size_t i = 0; i < len; ++i) { + switch (state) { + case 0: + if (stats_path[i] == '(') { + state = 1; + k = 0; + } + else if (stats_path[i] == ')') + return -1; + stats_path[j] = stats_path[i]; + j++; + break; + case 1: + if (stats_path[i] == ')') { + state = 0; + stats_path[j] = '#'; + j++; + stats_path[j] = ')'; + j++; + (*argc)++; + } + else { + argv[*argc][k++] = stats_path[i]; + } + break; + } + } + if (state == 1) + return -1; + stats_path[j] = 0; + return 0; +} + +uint64_t stats_parser_get(const char *stats_path) +{ + size_t stats_path_len; + + char stats_path_cpy[128]; + + strncpy(stats_path_cpy, stats_path, sizeof(stats_path_cpy)); + stats_path_len = strlen(stats_path); + + size_t max_argc = 16; + size_t argc = 0; + char argv_data[16][16] = {{0}}; + char *argv[16]; + const char *argv_c[16]; + + for (size_t i = 0; i < 16; ++i) { + argv[i] = argv_data[i]; + argv_c[i] = argv_data[i]; + } + + if (stats_parser_extract_args(stats_path_cpy, &argc, argv)) + return -1; + + for (size_t i = 0; i < sizeof(stats_paths)/sizeof(stats_paths[0]); ++i) { + if (strcmp(stats_paths[i].str, stats_path_cpy) == 0) { + if (stats_paths[i].func == NULL) + return -1; + return stats_paths[i].func(argc, argv_c); + } + } + + return -1; +} diff --git a/VNFs/DPPD-PROX/stats_parser.h b/VNFs/DPPD-PROX/stats_parser.h new file mode 100644 index 00000000..0812dbfc --- /dev/null +++ b/VNFs/DPPD-PROX/stats_parser.h @@ -0,0 +1,24 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_PARSER_H_ +#define _STATS_PARSER_H_ + +#include <inttypes.h> + +uint64_t stats_parser_get(const char *stats_path); + +#endif /* _STATS_PARSER_H_ */ diff --git a/VNFs/DPPD-PROX/stats_port.c b/VNFs/DPPD-PROX/stats_port.c new file mode 100644 index 00000000..b5e70dcc --- /dev/null +++ b/VNFs/DPPD-PROX/stats_port.c @@ -0,0 +1,407 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> + +#include <rte_version.h> +#include <rte_ethdev.h> +#include <rte_cycles.h> +#include <rte_byteorder.h> + +#include "prox_malloc.h" +#include "log.h" +#include "quit.h" +#include "stats_port.h" +#include "prox_port_cfg.h" +#include "rw_reg.h" + +#if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS) + +/* Directly access hardware counters instead of going through DPDK. This allows getting + * specific counters that DPDK does not report or aggregates with other ones. + */ + +/* Definitions for IXGBE (taken from PMD) */ +#define PROX_IXGBE_MPC(_i) (0x03FA0 + ((_i) * 4)) /* 8 of these 3FA0-3FBC*/ +#define PROX_IXGBE_QBRC_L(_i) (0x01034 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_QBRC_H(_i) (0x01038 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */ +#define PROX_IXGBE_GPTC 0x04080 +#define PROX_IXGBE_TPR 0x040D0 +#define PROX_IXGBE_TORL 0x040C0 +#define PROX_IXGBE_TORH 0x040C4 +#define PROX_IXGBE_GOTCL 0x04090 +#define PROX_IXGBE_GOTCH 0x04094 + +#define IXGBE_QUEUE_STAT_COUNTERS 16 + +static void ixgbe_read_stats(uint8_t port_id, struct port_stats_sample* stats, struct port_stats_sample *prev, int last_stat) +{ + uint64_t before, after; + unsigned i; + + struct rte_eth_dev* dev = &rte_eth_devices[port_id]; + + /* WARNING: Assumes hardware address is first field of structure! This may change! */ + struct _dev_hw* hw = (struct _dev_hw *)(dev->data->dev_private); + + stats->no_mbufs = dev->data->rx_mbuf_alloc_failed; + + /* Since we only read deltas from the NIC, we have to add to previous values + * even though we actually substract again later to find out the rates! + */ + stats->ierrors = prev->ierrors; + stats->imissed = prev->imissed; + stats->rx_bytes = prev->rx_bytes; + stats->rx_tot = prev->rx_tot; + stats->tx_bytes = prev->tx_bytes; + stats->tx_tot = prev->tx_tot; + + /* WARNING: In this implementation, we count as imiised only the "no descriptor" + * missed packets cases and not the actual receive errors. + */ + before = rte_rdtsc(); + for (i = 0; i < 8; i++) { + stats->imissed += PROX_READ_REG(hw, PROX_IXGBE_MPC(i)); + } + + /* RX stats */ +#if 0 + /* This version is equivalent to what ixgbe PMD does. It only accounts for packets + * actually received on the host. + */ + for (i = 0; i < IXGBE_QUEUE_STAT_COUNTERS; i++) { + /* ipackets: */ + stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_QPRC(i)); + /* ibytes: */ + stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_QBRC_L(i)); + stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_QBRC_H(i)) << 32); + } +#else + /* This version reports the packets received by the NIC, regardless of whether they + * reached the host or not, etc. (no need to add ierrors or imissedto this packet count) + */ + stats->rx_tot += PROX_READ_REG(hw, PROX_IXGBE_TPR); + stats->rx_bytes += PROX_READ_REG(hw, PROX_IXGBE_TORL); + stats->rx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_TORH) << 32); +#endif + + /* TX stats */ + /* opackets: */ + stats->tx_tot += PROX_READ_REG(hw, PROX_IXGBE_GPTC); + /* obytes: */ + stats->tx_bytes += PROX_READ_REG(hw, PROX_IXGBE_GOTCL); + stats->tx_bytes += ((uint64_t)PROX_READ_REG(hw, PROX_IXGBE_GOTCH) << 32); + after = rte_rdtsc(); + stats->tsc = (before >> 1) + (after >> 1); +} + +#endif + +extern int last_stat; +static struct port_stats port_stats[PROX_MAX_PORTS]; +static uint8_t nb_interface; +static uint8_t n_ports; +static int num_xstats[PROX_MAX_PORTS] = {0}; +static int num_ixgbe_xstats = 0; + +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,1) +#define XSTATS_SUPPORT 1 +#else +#define XSTATS_SUPPORT 0 +#endif + +#if XSTATS_SUPPORT +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) +static struct rte_eth_xstat *eth_xstats[PROX_MAX_PORTS] = {0}; +static struct rte_eth_xstat_name *eth_xstat_names[PROX_MAX_PORTS] = {0}; +#else +static struct rte_eth_xstats *eth_xstats[PROX_MAX_PORTS] = {0}; +static struct rte_eth_xstats *eth_xstat_names[PROX_MAX_PORTS] = {0}; +#endif +static int xstat_tpr_offset[PROX_MAX_PORTS] ={0}, xstat_tor_offset[PROX_MAX_PORTS] = {0}; +static int tx_pkt_size_offset[PROX_MAX_PORTS][PKT_SIZE_COUNT]; +#endif + +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) +static int find_xstats_str(struct rte_eth_xstat_name *xstats, int n, const char *name) +#else +static int find_xstats_str(struct rte_eth_xstats *xstats, int n, const char *name) +#endif +{ + for (int i = 0; i < n; i++) { + if (strcmp(xstats[i].name, name) == 0) + return i; + } + + return -1; +} + +void stats_port_init(void) +{ + int potential_ixgbe_warn = 0; + for (int i = 0; i < PROX_MAX_PORTS; i++) { + xstat_tpr_offset[i] = -1; + xstat_tor_offset[i] = -1; + for (int j = 0; j < PKT_SIZE_COUNT; j++) { + tx_pkt_size_offset[i][j] = -1; + } + } +#if XSTATS_SUPPORT + nb_interface = prox_last_port_active() + 1; + n_ports = prox_nb_active_ports(); + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) + num_xstats[port_id] = rte_eth_xstats_get_names(port_id, NULL, 0); + eth_xstat_names[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat_name), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstat_names[port_id] == NULL, "Error allocating memory for xstats"); + num_xstats[port_id] = rte_eth_xstats_get_names(port_id, eth_xstat_names[port_id], num_xstats[port_id]); + eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstat), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats"); +#else + num_xstats[port_id] = rte_eth_xstats_get(port_id, NULL, 0); + eth_xstats[port_id] = prox_zmalloc(num_xstats[port_id] * sizeof(struct rte_eth_xstats), prox_port_cfg[port_id].socket); + PROX_PANIC(eth_xstats[port_id] == NULL, "Error allocating memory for xstats"); + eth_xstat_names[port_id] = eth_xstats[port_id]; + num_xstats[port_id] = rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]); +#endif + if (!strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) { + potential_ixgbe_warn = 1; + xstat_tor_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_bytes"); + xstat_tpr_offset[port_id] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "rx_total_packets"); + } + tx_pkt_size_offset[port_id][PKT_SIZE_64] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_64_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_65] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_65_to_127_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_128] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_128_to_255_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_256] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_256_to_511_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_512] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_512_to_1023_packets"); + if (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe")) { + tx_pkt_size_offset[port_id][PKT_SIZE_1024] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1024_to_max_packets"); + } else { + tx_pkt_size_offset[port_id][PKT_SIZE_1024] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1024_to_1522_packets"); + tx_pkt_size_offset[port_id][PKT_SIZE_1522] = find_xstats_str(eth_xstat_names[port_id], num_xstats[port_id], "tx_size_1523_to_max_packets"); + } + plog_info("offset = %d, %d, %d, %d, %d, %d %d\n", tx_pkt_size_offset[port_id][PKT_SIZE_64], tx_pkt_size_offset[port_id][PKT_SIZE_65], tx_pkt_size_offset[port_id][PKT_SIZE_128], tx_pkt_size_offset[port_id][PKT_SIZE_256], tx_pkt_size_offset[port_id][PKT_SIZE_512], tx_pkt_size_offset[port_id][PKT_SIZE_1024], tx_pkt_size_offset[port_id][PKT_SIZE_1522]); +#if RTE_VERSION >= RTE_VERSION_NUM(16,7,0,0) + prox_free(eth_xstat_names[port_id]); +#endif + if (num_xstats[port_id] == 0 || eth_xstats[port_id] == NULL) { + plog_warn("Failed to initialize xstat for port %d, running without xstats\n", port_id); + num_xstats[port_id] = 0; + } + } + } + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if ((xstat_tor_offset[port_id] != -1) && (xstat_tpr_offset[port_id] != -1)) { + num_ixgbe_xstats = 2; // ixgbe PMD supports tor and tpr xstats + break; + } + } + if ((num_ixgbe_xstats == 0) && (potential_ixgbe_warn)) + plog_warn("Failed to initialize ixgbe xstat, running without ixgbe xstats\n"); +#endif +} + +static void nic_read_stats(uint8_t port_id) +{ + unsigned is_ixgbe = (0 == strcmp(prox_port_cfg[port_id].short_name, "ixgbe")); + + struct port_stats_sample *stats = &port_stats[port_id].sample[last_stat]; + +#if defined(PROX_STATS) && defined(PROX_HW_DIRECT_STATS) + if (is_ixgbe) { + struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat]; + ixgbe_read_stats(port_id, stats, prev, last_stat); + return; + } +#endif + uint64_t before, after; + + struct rte_eth_stats eth_stat; + + before = rte_rdtsc(); + rte_eth_stats_get(port_id, ð_stat); + after = rte_rdtsc(); + + stats->tsc = (before >> 1) + (after >> 1); + stats->no_mbufs = eth_stat.rx_nombuf; + stats->ierrors = eth_stat.ierrors; + stats->imissed = eth_stat.imissed; + stats->oerrors = eth_stat.oerrors; + stats->rx_bytes = eth_stat.ibytes; + + /* The goal would be to get the total number of bytes received + by the NIC (including overhead). Without the patch + (i.e. num_ixgbe_xstats == 0) we can't do this directly with + DPDK 2.1 API. So, we report the number of bytes (including + overhead) received by the host. */ + +#if XSTATS_SUPPORT + if (num_xstats[port_id]) { + rte_eth_xstats_get(port_id, eth_xstats[port_id], num_xstats[port_id]); + for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) { + if (tx_pkt_size_offset[port_id][i] != -1) + stats->tx_pkt_size[i] = (eth_xstats[port_id][tx_pkt_size_offset[port_id][i]]).value; + else + stats->tx_pkt_size[i] = -1; + } + } else { + for (size_t i = 0; i < sizeof(tx_pkt_size_offset[0])/sizeof(tx_pkt_size_offset[0][0]); ++i) { + stats->tx_pkt_size[i] = -1; + } + } +#endif + if (is_ixgbe) { +#if XSTATS_SUPPORT + if (num_ixgbe_xstats) { + stats->rx_tot = eth_xstats[port_id][xstat_tpr_offset[port_id]].value; + stats->rx_bytes = eth_xstats[port_id][xstat_tor_offset[port_id]].value; + } else +#endif + { + stats->rx_tot = eth_stat.ipackets + eth_stat.ierrors + eth_stat.imissed; + /* On ixgbe, the rx_bytes counts bytes + received by Host without overhead. The + rx_tot counts the number of packets + received by the NIC. If we only add 20 * + rx_tot to rx_bytes, the result will also + take into account 20 * "number of packets + dropped by the nic". Note that in case CRC + is stripped on ixgbe, the CRC bytes are not + counted. */ + if (prox_port_cfg[port_id].port_conf.rxmode.hw_strip_crc == 1) + stats->rx_bytes = eth_stat.ibytes + + (24 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed)); + else + stats->rx_bytes = eth_stat.ibytes + + (20 * eth_stat.ipackets - 20 * (eth_stat.ierrors + eth_stat.imissed)); + } + } else if (strcmp(prox_port_cfg[port_id].short_name, "i40e_vf") == 0) { + // For I40E VF, imissed already part of received packets + stats->rx_tot = eth_stat.ipackets; + } else { + stats->rx_tot = eth_stat.ipackets + eth_stat.imissed; + } + stats->tx_tot = eth_stat.opackets; + stats->tx_bytes = eth_stat.obytes; +} + +void stats_port_reset(void) +{ + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { + rte_eth_stats_reset(port_id); + memset(&port_stats[port_id], 0, sizeof(struct port_stats)); + } + } +} + +void stats_port_update(void) +{ + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) { + nic_read_stats(port_id); + } + } +} + +uint64_t stats_port_get_ierrors(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].ierrors; + } + return ret; +} + +uint64_t stats_port_get_imissed(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].imissed; + } + return ret; +} + +uint64_t stats_port_get_rx_packets(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].rx_tot; + } + return ret; +} + +uint64_t stats_port_get_tx_packets(void) +{ + uint64_t ret = 0; + + for (uint8_t port_id = 0; port_id < nb_interface; ++port_id) { + if (prox_port_cfg[port_id].active) + ret += port_stats[port_id].sample[last_stat].tx_tot; + } + return ret; +} + +int stats_get_n_ports(void) +{ + return n_ports; +} + +struct port_stats_sample *stats_get_port_stats_sample(uint32_t port_id, int l) +{ + return &port_stats[port_id].sample[l == last_stat]; +} + +int stats_port(uint8_t port_id, struct get_port_stats *gps) +{ + if (!prox_port_cfg[port_id].active) + return -1; + + struct port_stats_sample *last = &port_stats[port_id].sample[last_stat]; + struct port_stats_sample *prev = &port_stats[port_id].sample[!last_stat]; + + gps->no_mbufs_diff = last->no_mbufs - prev->no_mbufs; + gps->ierrors_diff = last->ierrors - prev->ierrors; + gps->imissed_diff = last->imissed - prev->imissed; + gps->rx_bytes_diff = last->rx_bytes - prev->rx_bytes; + gps->tx_bytes_diff = last->tx_bytes - prev->tx_bytes; + gps->rx_pkts_diff = last->rx_tot - prev->rx_tot; + if (unlikely(prev->rx_tot > last->rx_tot)) + gps->rx_pkts_diff = 0; + gps->tx_pkts_diff = last->tx_tot - prev->tx_tot; + if (unlikely(prev->tx_tot > last->tx_tot)) + gps->rx_pkts_diff = 0; + gps->rx_tot = last->rx_tot; + gps->tx_tot = last->tx_tot; + gps->no_mbufs_tot = last->no_mbufs; + gps->ierrors_tot = last->ierrors; + gps->imissed_tot = last->imissed; + + gps->last_tsc = last->tsc; + gps->prev_tsc = prev->tsc; + + return 0; +} diff --git a/VNFs/DPPD-PROX/stats_port.h b/VNFs/DPPD-PROX/stats_port.h new file mode 100644 index 00000000..4e166e1b --- /dev/null +++ b/VNFs/DPPD-PROX/stats_port.h @@ -0,0 +1,79 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_PORT_H_ +#define _STATS_PORT_H_ + +#include <inttypes.h> + +enum PKT_SIZE_BIN { + PKT_SIZE_64, + PKT_SIZE_65, + PKT_SIZE_128, + PKT_SIZE_256, + PKT_SIZE_512, + PKT_SIZE_1024, + PKT_SIZE_1522, + PKT_SIZE_COUNT, +}; + +struct port_stats_sample { + uint64_t tsc; + uint64_t no_mbufs; + uint64_t ierrors; + uint64_t imissed; + uint64_t oerrors; + uint64_t rx_tot; + uint64_t tx_tot; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t tx_pkt_size[PKT_SIZE_COUNT]; +}; + +struct port_stats { + struct port_stats_sample sample[2]; +}; + +struct get_port_stats { + uint64_t no_mbufs_diff; + uint64_t ierrors_diff; + uint64_t imissed_diff; + uint64_t rx_bytes_diff; + uint64_t tx_bytes_diff; + uint64_t rx_pkts_diff; + uint64_t tx_pkts_diff; + uint64_t rx_tot; + uint64_t tx_tot; + uint64_t no_mbufs_tot; + uint64_t ierrors_tot; + uint64_t imissed_tot; + uint64_t last_tsc; + uint64_t prev_tsc; +}; + +int stats_port(uint8_t port_id, struct get_port_stats *ps); +void stats_port_init(void); +void stats_port_reset(void); +void stats_port_update(void); +uint64_t stats_port_get_ierrors(void); +uint64_t stats_port_get_imissed(void); +uint64_t stats_port_get_rx_packets(void); +uint64_t stats_port_get_tx_packets(void); + +int stats_get_n_ports(void); +struct port_stats_sample *stats_get_port_stats_sample(uint32_t port_id, int l); + +#endif /* _STATS_PORT_H_ */ diff --git a/VNFs/DPPD-PROX/stats_prio.c b/VNFs/DPPD-PROX/stats_prio.c new file mode 100644 index 00000000..3d39d580 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_prio.c @@ -0,0 +1,131 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stddef.h> + +#include "handle_aggregator.h" +#include "stats_prio_task.h" +#include "prox_cfg.h" +#include "prox_globals.h" +#include "lconf.h" + +struct lcore_task_stats { + struct task_stats task_stats[MAX_TASKS_PER_CORE]; +}; + +struct lcore_prio_task_stats { + struct prio_task_stats prio_task_stats[MAX_TASKS_PER_CORE]; +}; + +extern int last_stat; +static struct prio_task_stats prio_task_stats_set[RTE_MAX_LCORE * MAX_TASKS_PER_CORE]; +static uint8_t nb_prio_tasks_tot; + +int stats_get_n_prio_tasks_tot(void) +{ + return nb_prio_tasks_tot; +} + +struct prio_task_stats_sample *stats_get_prio_task_stats_sample(uint32_t prio_task_id, int l) +{ + return &prio_task_stats_set[prio_task_id].sample[l == last_stat]; +} + +struct prio_task_stats_sample *stats_get_prio_task_stats_sample_by_core_task(uint32_t lcore_id, uint32_t prio_task_id, int l) +{ + for (uint8_t task_id = 0; task_id < nb_prio_tasks_tot; ++task_id) { + if ((prio_task_stats_set[task_id].lcore_id == lcore_id) && (prio_task_stats_set[task_id].task_id == task_id)) + return &prio_task_stats_set[prio_task_id].sample[l == last_stat]; + } + return NULL; +} + +void stats_prio_task_reset(void) +{ + struct prio_task_stats *cur_task_stats; + + for (uint8_t task_id = 0; task_id < nb_prio_tasks_tot; ++task_id) { + cur_task_stats = &prio_task_stats_set[task_id]; + for (int i = 0; i < 8; i++) { + cur_task_stats->tot_drop_tx_fail_prio[i] = 0; + cur_task_stats->tot_rx_prio[i] = 0; + } + } +} + +uint64_t stats_core_task_tot_drop_tx_fail_prio(uint8_t prio_task_id, uint8_t prio) +{ + return prio_task_stats_set[prio_task_id].tot_drop_tx_fail_prio[prio]; +} + +uint64_t stats_core_task_tot_rx_prio(uint8_t prio_task_id, uint8_t prio) +{ + return prio_task_stats_set[prio_task_id].tot_rx_prio[prio]; +} + +void stats_prio_task_post_proc(void) +{ + for (uint8_t task_id = 0; task_id < nb_prio_tasks_tot; ++task_id) { + struct prio_task_stats *cur_task_stats = &prio_task_stats_set[task_id]; + const struct prio_task_stats_sample *last = &cur_task_stats->sample[last_stat]; + const struct prio_task_stats_sample *prev = &cur_task_stats->sample[!last_stat]; + + for (int i=0; i<8; i++) { + cur_task_stats->tot_rx_prio[i] += last->rx_prio[i] - prev->rx_prio[i]; + cur_task_stats->tot_drop_tx_fail_prio[i] += last->drop_tx_fail_prio[i] - prev->drop_tx_fail_prio[i]; + } + } +} + +void stats_prio_task_update(void) +{ + uint64_t before, after; + + for (uint8_t task_id = 0; task_id < nb_prio_tasks_tot; ++task_id) { + struct prio_task_stats *cur_task_stats = &prio_task_stats_set[task_id]; + struct prio_task_rt_stats *stats = cur_task_stats->stats; + struct prio_task_stats_sample *last = &cur_task_stats->sample[last_stat]; + + before = rte_rdtsc(); + for (int i=0; i<8; i++) { + last->drop_tx_fail_prio[i] = stats->drop_tx_fail_prio[i]; + last->rx_prio[i] = stats->rx_prio[i]; + } + after = rte_rdtsc(); + last->tsc = (before >> 1) + (after >> 1); + } +} + +void stats_prio_task_init(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id; + + /* add cores that are receiving from and sending to physical ports first */ + lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + if (strcmp(targ->task_init->mode_str, "aggreg") == 0) { + struct prio_task_rt_stats *stats = &((struct task_aggregator *)(lconf->tasks_all[task_id]))->stats; + prio_task_stats_set[nb_prio_tasks_tot].stats = stats; + prio_task_stats_set[nb_prio_tasks_tot].lcore_id = lcore_id; + prio_task_stats_set[nb_prio_tasks_tot++].task_id = task_id; + } + } + } +} diff --git a/VNFs/DPPD-PROX/stats_prio_task.h b/VNFs/DPPD-PROX/stats_prio_task.h new file mode 100644 index 00000000..ce150591 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_prio_task.h @@ -0,0 +1,55 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_PRIO_TASK_H_ +#define _STATS_PRIO_TASK_H_ + +#include <inttypes.h> + +#include "clock.h" + +struct prio_task_stats_sample { + uint64_t tsc; + uint64_t drop_tx_fail_prio[8]; + uint64_t rx_prio[8]; +}; + +struct prio_task_rt_stats { + uint64_t drop_tx_fail_prio[8]; + uint64_t rx_prio[8]; +}; + +struct prio_task_stats { + uint64_t tot_drop_tx_fail_prio[8]; + uint64_t tot_rx_prio[8]; + uint8_t lcore_id; + uint8_t task_id; + struct prio_task_stats_sample sample[2]; + struct prio_task_rt_stats *stats; +}; + +int stats_get_n_prio_tasks_tot(void); +void stats_prio_task_reset(void); +void stats_prio_task_post_proc(void); +void stats_prio_task_update(void); +void stats_prio_task_init(void); + +struct prio_task_stats_sample *stats_get_prio_task_stats_sample(uint32_t task_id, int last); +struct prio_task_stats_sample *stats_get_prio_task_stats_sample_by_core_task(uint32_t lcore_id, uint32_t task_id, int last); +uint64_t stats_core_task_tot_drop_tx_fail_prio(uint8_t task_id, uint8_t prio); +uint64_t stats_core_task_tot_rx_prio(uint8_t task_id, uint8_t prio); + +#endif /* _STATS_PRIO_TASK_H_ */ diff --git a/VNFs/DPPD-PROX/stats_ring.c b/VNFs/DPPD-PROX/stats_ring.c new file mode 100644 index 00000000..d0792ac1 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_ring.c @@ -0,0 +1,160 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <rte_ring.h> +#include <rte_version.h> + +#include "prox_malloc.h" +#include "stats_ring.h" +#include "prox_port_cfg.h" +#include "prox_cfg.h" +#include "lconf.h" +#include "log.h" +#include "quit.h" + +struct stats_ring_manager { + uint16_t n_rings; + struct ring_stats ring_stats[0]; +}; + +static struct stats_ring_manager *rsm; + +int stats_get_n_rings(void) +{ + return rsm->n_rings; +} + +struct ring_stats *stats_get_ring_stats(uint32_t i) +{ + return &rsm->ring_stats[i]; +} + +void stats_ring_update(void) +{ + for (uint16_t r_id = 0; r_id < rsm->n_rings; ++r_id) { + rsm->ring_stats[r_id].free = rte_ring_free_count(rsm->ring_stats[r_id].ring); + } +} + +static struct ring_stats *init_rings_add(struct stats_ring_manager *rsm, struct rte_ring *ring) +{ + for (uint16_t i = 0; i < rsm->n_rings; ++i) { + if (strcmp(ring->name, rsm->ring_stats[i].ring->name) == 0) + return &rsm->ring_stats[i]; + } + rsm->ring_stats[rsm->n_rings++].ring = ring; + return &rsm->ring_stats[rsm->n_rings - 1]; +} + +static struct stats_ring_manager *alloc_stats_ring_manager(void) +{ + const uint32_t socket_id = rte_lcore_to_socket_id(rte_lcore_id()); + struct lcore_cfg *lconf; + uint32_t lcore_id = -1; + uint32_t n_rings = 0; + struct task_args *targ; + + /* n_rings could be more than total number of rings since + rings could be referenced by multiple cores. */ + while(prox_core_next(&lcore_id, 1) == 0) { + lconf = &lcore_cfg[lcore_id]; + + for(uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + targ = &lconf->targs[task_id]; + + for(uint32_t rxring_id = 0; rxring_id < targ->nb_rxrings; ++rxring_id) { + if (!targ->tx_opt_ring_task) + n_rings++; + } + for (uint32_t txring_id = 0; txring_id < targ->nb_txrings; ++txring_id) { + if (!targ->tx_opt_ring) + n_rings++; + } + } + } + + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + if (!prox_port_cfg[port_id].active) { + continue; + } + + if (prox_port_cfg[port_id].rx_ring[0] != '\0') + n_rings++; + + if (prox_port_cfg[port_id].tx_ring[0] != '\0') + n_rings++; + } + + size_t mem_size = sizeof(struct stats_ring_manager) + + n_rings * sizeof(struct ring_stats); + + return prox_zmalloc(mem_size, socket_id); +} + +void stats_ring_init(void) +{ + uint32_t lcore_id = -1; + struct lcore_cfg *lconf; + struct task_args *targ; + + rsm = alloc_stats_ring_manager(); + while(prox_core_next(&lcore_id, 1) == 0) { + lconf = &lcore_cfg[lcore_id]; + + for(uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + targ = &lconf->targs[task_id]; + + for(uint32_t rxring_id = 0; rxring_id < targ->nb_rxrings; ++rxring_id) { + if (!targ->tx_opt_ring_task) + init_rings_add(rsm, targ->rx_rings[rxring_id]); + } + + for (uint32_t txring_id = 0; txring_id < targ->nb_txrings; ++txring_id) { + if (!targ->tx_opt_ring) + init_rings_add(rsm, targ->tx_rings[txring_id]); + } + } + } + + struct ring_stats *stats = NULL; + + for (uint8_t port_id = 0; port_id < PROX_MAX_PORTS; ++port_id) { + if (!prox_port_cfg[port_id].active) { + continue; + } + + if (prox_port_cfg[port_id].rx_ring[0] != '\0') { + stats = init_rings_add(rsm, rte_ring_lookup(prox_port_cfg[port_id].rx_ring)); + stats->port[stats->nb_ports++] = &prox_port_cfg[port_id]; + } + + if (prox_port_cfg[port_id].tx_ring[0] != '\0') { + stats = init_rings_add(rsm, rte_ring_lookup(prox_port_cfg[port_id].tx_ring)); + stats->port[stats->nb_ports++] = &prox_port_cfg[port_id]; + } + } + + /* The actual usable space for a ring is size - 1. There is at + most one free entry in the ring to distinguish between + full/empty. */ + for (uint16_t ring_id = 0; ring_id < rsm->n_rings; ++ring_id) +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + rsm->ring_stats[ring_id].size = rsm->ring_stats[ring_id].ring->prod.size - 1; +#else + rsm->ring_stats[ring_id].size = rsm->ring_stats[ring_id].ring->size - 1; +#endif +} diff --git a/VNFs/DPPD-PROX/stats_ring.h b/VNFs/DPPD-PROX/stats_ring.h new file mode 100644 index 00000000..d9d4d63f --- /dev/null +++ b/VNFs/DPPD-PROX/stats_ring.h @@ -0,0 +1,34 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "prox_globals.h" + +struct rte_ring; +struct prox_port_cfg; + +struct ring_stats { + struct rte_ring *ring; + uint32_t nb_ports; + struct prox_port_cfg *port[PROX_MAX_PORTS]; + uint32_t free; + uint32_t size; +}; + +void stats_ring_update(void); +void stats_ring_init(void); + +int stats_get_n_rings(void); +struct ring_stats *stats_get_ring_stats(uint32_t i); diff --git a/VNFs/DPPD-PROX/stats_task.c b/VNFs/DPPD-PROX/stats_task.c new file mode 100644 index 00000000..6b4dc2dd --- /dev/null +++ b/VNFs/DPPD-PROX/stats_task.c @@ -0,0 +1,227 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stddef.h> + +#include "stats_task.h" +#include "prox_cfg.h" +#include "prox_globals.h" +#include "lconf.h" + +struct lcore_task_stats { + struct task_stats task_stats[MAX_TASKS_PER_CORE]; +}; + +#define TASK_STATS_RX 0x01 +#define TASK_STATS_TX 0x02 + +extern int last_stat; +static struct lcore_task_stats lcore_task_stats_all[RTE_MAX_LCORE]; +static struct task_stats *task_stats_set[RTE_MAX_LCORE * MAX_TASKS_PER_CORE]; +static uint8_t nb_tasks_tot; +int stats_get_n_tasks_tot(void) +{ + return nb_tasks_tot; +} + +struct task_stats *stats_get_task_stats(uint32_t lcore_id, uint32_t task_id) +{ + return &lcore_task_stats_all[lcore_id].task_stats[task_id]; +} + +struct task_stats_sample *stats_get_task_stats_sample(uint32_t lcore_id, uint32_t task_id, int l) +{ + return &lcore_task_stats_all[lcore_id].task_stats[task_id].sample[l == last_stat]; +} + +void stats_task_reset(void) +{ + struct task_stats *cur_task_stats; + + for (uint8_t task_id = 0; task_id < nb_tasks_tot; ++task_id) { + cur_task_stats = task_stats_set[task_id]; + cur_task_stats->tot_rx_pkt_count = 0; + cur_task_stats->tot_tx_pkt_count = 0; + cur_task_stats->tot_drop_tx_fail = 0; + cur_task_stats->tot_drop_discard = 0; + cur_task_stats->tot_drop_handled = 0; + } +} + +uint64_t stats_core_task_tot_rx(uint8_t lcore_id, uint8_t task_id) +{ + return lcore_task_stats_all[lcore_id].task_stats[task_id].tot_rx_pkt_count; +} + +uint64_t stats_core_task_tot_tx(uint8_t lcore_id, uint8_t task_id) +{ + return lcore_task_stats_all[lcore_id].task_stats[task_id].tot_tx_pkt_count; +} + +uint64_t stats_core_task_tot_drop(uint8_t lcore_id, uint8_t task_id) +{ + return lcore_task_stats_all[lcore_id].task_stats[task_id].tot_drop_tx_fail + + lcore_task_stats_all[lcore_id].task_stats[task_id].tot_drop_discard + + lcore_task_stats_all[lcore_id].task_stats[task_id].tot_drop_handled; +} + +uint64_t stats_core_task_last_tsc(uint8_t lcore_id, uint8_t task_id) +{ + return lcore_task_stats_all[lcore_id].task_stats[task_id].sample[last_stat].tsc; +} + +static void init_core_port(struct task_stats *ts, struct task_rt_stats *stats, uint8_t flags) +{ + ts->stats = stats; + ts->flags |= flags; +} + +void stats_task_post_proc(void) +{ + for (uint8_t task_id = 0; task_id < nb_tasks_tot; ++task_id) { + struct task_stats *cur_task_stats = task_stats_set[task_id]; + const struct task_stats_sample *last = &cur_task_stats->sample[last_stat]; + const struct task_stats_sample *prev = &cur_task_stats->sample[!last_stat]; + + /* no total stats for empty loops */ + cur_task_stats->tot_rx_pkt_count += last->rx_pkt_count - prev->rx_pkt_count; + cur_task_stats->tot_tx_pkt_count += last->tx_pkt_count - prev->tx_pkt_count; + cur_task_stats->tot_drop_tx_fail += last->drop_tx_fail - prev->drop_tx_fail; + cur_task_stats->tot_drop_discard += last->drop_discard - prev->drop_discard; + cur_task_stats->tot_drop_handled += last->drop_handled - prev->drop_handled; + } +} + +void stats_task_update(void) +{ + uint64_t before, after; + + for (uint8_t task_id = 0; task_id < nb_tasks_tot; ++task_id) { + struct task_stats *cur_task_stats = task_stats_set[task_id]; + struct task_rt_stats *stats = cur_task_stats->stats; + struct task_stats_sample *last = &cur_task_stats->sample[last_stat]; + + /* Read TX first and RX second, in order to prevent displaying + a negative packet loss. Depending on the configuration + (when forwarding, for example), TX might be bigger than RX. */ + before = rte_rdtsc(); + last->tx_pkt_count = stats->tx_pkt_count; + last->drop_tx_fail = stats->drop_tx_fail; + last->drop_discard = stats->drop_discard; + last->drop_handled = stats->drop_handled; + last->rx_pkt_count = stats->rx_pkt_count; + last->empty_cycles = stats->idle_cycles; + last->tx_bytes = stats->tx_bytes; + last->rx_bytes = stats->rx_bytes; + last->drop_bytes = stats->drop_bytes; + after = rte_rdtsc(); + last->tsc = (before >> 1) + (after >> 1); + } +} + +void stats_task_get_host_rx_tx_packets(uint64_t *rx, uint64_t *tx, uint64_t *tsc) +{ + const struct task_stats *t; + + *rx = 0; + *tx = 0; + + for (uint8_t task_id = 0; task_id < nb_tasks_tot; ++task_id) { + t = task_stats_set[task_id]; + + if (t->flags & TASK_STATS_RX) + *rx += t->tot_rx_pkt_count; + + if (t->flags & TASK_STATS_TX) + *tx += t->tot_tx_pkt_count; + } + if (nb_tasks_tot) + *tsc = task_stats_set[nb_tasks_tot - 1]->sample[last_stat].tsc; +} + +/* Populate active_stats_set for stats reporting, the order of the + cores is important for gathering the most accurate statistics. TX + cores should be updated before RX cores (to prevent negative Loss + stats). The total number of tasks are saved in nb_tasks_tot. */ +void stats_task_init(void) +{ + struct lcore_cfg *lconf; + uint32_t lcore_id; + + /* add cores that are receiving from and sending to physical ports first */ + lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + struct task_rt_stats *stats = &lconf->tasks_all[task_id]->aux->stats; + if (targ->nb_rxrings == 0 && targ->nb_txrings == 0) { + struct task_stats *ts = &lcore_task_stats_all[lcore_id].task_stats[task_id]; + + init_core_port(ts, stats, TASK_STATS_RX | TASK_STATS_TX); + task_stats_set[nb_tasks_tot++] = ts; + } + } + } + + /* add cores that are sending to physical ports second */ + lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + struct task_rt_stats *stats = &lconf->tasks_all[task_id]->aux->stats; + if (targ->nb_rxrings != 0 && targ->nb_txrings == 0) { + struct task_stats *ts = &lcore_task_stats_all[lcore_id].task_stats[task_id]; + + init_core_port(ts, stats, TASK_STATS_TX); + task_stats_set[nb_tasks_tot++] = ts; + } + } + } + + /* add cores that are receiving from physical ports third */ + lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + struct task_rt_stats *stats = &lconf->tasks_all[task_id]->aux->stats; + if (targ->nb_rxrings == 0 && targ->nb_txrings != 0) { + struct task_stats *ts = &lcore_task_stats_all[lcore_id].task_stats[task_id]; + + init_core_port(ts, stats, TASK_STATS_RX); + task_stats_set[nb_tasks_tot++] = ts; + } + } + } + + /* add cores that are working internally (no physical ports attached) */ + lcore_id = -1; + while(prox_core_next(&lcore_id, 0) == 0) { + lconf = &lcore_cfg[lcore_id]; + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + struct task_rt_stats *stats = &lconf->tasks_all[task_id]->aux->stats; + if (targ->nb_rxrings != 0 && targ->nb_txrings != 0) { + struct task_stats *ts = &lcore_task_stats_all[lcore_id].task_stats[task_id]; + + init_core_port(ts, stats, 0); + task_stats_set[nb_tasks_tot++] = ts; + } + } + } +} diff --git a/VNFs/DPPD-PROX/stats_task.h b/VNFs/DPPD-PROX/stats_task.h new file mode 100644 index 00000000..156eb326 --- /dev/null +++ b/VNFs/DPPD-PROX/stats_task.h @@ -0,0 +1,145 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STATS_TASK_H_ +#define _STATS_TASK_H_ + +#include <inttypes.h> + +#include "clock.h" + +/* The struct task_stats is read/write from the task itself and + read-only from the core that collects the stats. Since only the + task executing the actual work ever modifies the stats, no locking + is required. Both a read and a write are atomic (assuming the + correct alignment). From this, it followed that the statistics can + be incremented directly by the task itself. In cases where these + assumptions do not hold, a possible solution (although slightly + less accurate) would be to keep accumulate statistics temporarily + in a separate structure and periodically copying the statistics to + the statistics core through atomic primitives, for example through + rte_atomic32_set(). The accuracy would be determined by the + frequency at which the statistics are transferred to the statistics + core. */ + +struct task_rt_stats { + uint32_t rx_pkt_count; + uint32_t tx_pkt_count; + uint32_t drop_tx_fail; + uint32_t drop_discard; + uint32_t drop_handled; + uint32_t idle_cycles; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t drop_bytes; +} __attribute__((packed)) __rte_cache_aligned; + +#ifdef PROX_STATS +#define TASK_STATS_ADD_IDLE(stats, cycles) do { \ + (stats)->idle_cycles += (cycles) + rdtsc_overhead_stats; \ + } while(0) \ + +#define TASK_STATS_ADD_TX(stats, ntx) do { \ + (stats)->tx_pkt_count += ntx; \ + } while(0) \ + +#define TASK_STATS_ADD_DROP_TX_FAIL(stats, ntx) do { \ + (stats)->drop_tx_fail += ntx; \ + } while(0) \ + +#define TASK_STATS_ADD_DROP_HANDLED(stats, ntx) do { \ + (stats)->drop_handled += ntx; \ + } while(0) \ + +#define TASK_STATS_ADD_DROP_DISCARD(stats, ntx) do { \ + (stats)->drop_discard += ntx; \ + } while(0) \ + +#define TASK_STATS_ADD_RX(stats, ntx) do { \ + (stats)->rx_pkt_count += ntx; \ + } while (0) \ + +#define TASK_STATS_ADD_RX_BYTES(stats, bytes) do { \ + (stats)->rx_bytes += bytes; \ + } while (0) \ + +#define TASK_STATS_ADD_TX_BYTES(stats, bytes) do { \ + (stats)->tx_bytes += bytes; \ + } while (0) \ + +#define TASK_STATS_ADD_DROP_BYTES(stats, bytes) do { \ + (stats)->drop_bytes += bytes; \ + } while (0) \ + +#define START_EMPTY_MEASSURE() uint64_t cur_tsc = rte_rdtsc(); +#else +#define TASK_STATS_ADD_IDLE(stats, cycles) do {} while(0) +#define TASK_STATS_ADD_TX(stats, ntx) do {} while(0) +#define TASK_STATS_ADD_DROP_TX_FAIL(stats, ntx) do {} while(0) +#define TASK_STATS_ADD_DROP_HANDLED(stats, ntx) do {} while(0) +#define TASK_STATS_ADD_DROP_DISCARD(stats, ntx) do {} while(0) +#define TASK_STATS_ADD_RX(stats, ntx) do {} while(0) +#define TASK_STATS_ADD_RX_BYTES(stats, bytes) do {} while(0) +#define TASK_STATS_ADD_TX_BYTES(stats, bytes) do {} while(0) +#define TASK_STATS_ADD_DROP_BYTES(stats, bytes) do {} while(0) +#define START_EMPTY_MEASSURE() do {} while(0) +#endif + +struct task_stats_sample { + uint64_t tsc; + uint32_t tx_pkt_count; + uint32_t drop_tx_fail; + uint32_t drop_discard; + uint32_t drop_handled; + uint32_t rx_pkt_count; + uint32_t empty_cycles; + uint64_t rx_bytes; + uint64_t tx_bytes; + uint64_t drop_bytes; +}; + +struct task_stats { + uint64_t tot_tx_pkt_count; + uint64_t tot_drop_tx_fail; + uint64_t tot_drop_discard; + uint64_t tot_drop_handled; + uint64_t tot_rx_pkt_count; + + struct task_stats_sample sample[2]; + + struct task_rt_stats *stats; + /* flags set if total RX/TX values need to be reported set at + initialization time, only need to access stats values in port */ + uint8_t flags; +}; + +void stats_task_reset(void); +void stats_task_post_proc(void); +void stats_task_update(void); +void stats_task_init(void); + +int stats_get_n_tasks_tot(void); + +struct task_stats *stats_get_task_stats(uint32_t lcore_id, uint32_t task_id); +struct task_stats_sample *stats_get_task_stats_sample(uint32_t lcore_id, uint32_t task_id, int last); +void stats_task_get_host_rx_tx_packets(uint64_t *rx, uint64_t *tx, uint64_t *tsc); + +uint64_t stats_core_task_tot_rx(uint8_t lcore_id, uint8_t task_id); +uint64_t stats_core_task_tot_tx(uint8_t lcore_id, uint8_t task_id); +uint64_t stats_core_task_tot_drop(uint8_t lcore_id, uint8_t task_id); +uint64_t stats_core_task_last_tsc(uint8_t lcore_id, uint8_t task_id); + +#endif /* _STATS_TASK_H_ */ diff --git a/VNFs/DPPD-PROX/task_base.h b/VNFs/DPPD-PROX/task_base.h new file mode 100644 index 00000000..b2fab2fc --- /dev/null +++ b/VNFs/DPPD-PROX/task_base.h @@ -0,0 +1,247 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TASK_BASE_H_ +#define _TASK_BASE_H_ + +#include <rte_common.h> +#ifndef __rte_cache_aligned +#include <rte_memory.h> +#endif + +#include "defaults.h" +#include "prox_globals.h" +#include "stats_task.h" + +// runtime_flags 8 bits only +#define TASK_MPLS_TAGGING 0x0001 +#define TASK_ROUTING 0x0002 +#define TASK_CLASSIFY 0x0004 +#define TASK_CTRL_HANDLE_ARP 0x0008 +#define TASK_MARK 0x0020 +#define TASK_FP_HANDLE_ARP 0x0040 +#define TASK_TX_CRC 0x0080 + +// flag_features 64 bits +#define TASK_FEATURE_ROUTING 0x0001 +#define TASK_FEATURE_CLASSIFY 0x0002 +#define TASK_FEATURE_MULTI_RX 0x0004 +#define TASK_FEATURE_NEVER_DISCARDS 0x0008 +#define TASK_FEATURE_NO_RX 0x0010 +#define TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS 0x0020 +#define TASK_FEATURE_TXQ_FLAGS_NOMULTSEGS 0x0040 +#define TASK_FEATURE_ZERO_RX 0x0080 +#define TASK_FEATURE_TXQ_FLAGS_REFCOUNT 0x0100 +#define TASK_FEATURE_TSC_RX 0x0200 +#define TASK_FEATURE_THROUGHPUT_OPT 0x0400 +#define TASK_FEATURE_GRE_ID 0x1000 +#define TASK_FEATURE_LUT_QINQ_RSS 0x2000 +#define TASK_FEATURE_LUT_QINQ_HASH 0x4000 +#define TASK_FEATURE_RX_ALL 0x8000 +#define TASK_MULTIPLE_MAC 0x10000 + +#define FLAG_TX_FLUSH 0x01 +#define FLAG_NEVER_FLUSH 0x02 +// Task specific flags +#define BASE_FLAG_LUT_QINQ_HASH 0x08 +#define BASE_FLAG_LUT_QINQ_RSS 0x10 + +#define OUT_DISCARD 0xFF +#define OUT_HANDLED 0xFE + +#define WS_MBUF_MASK (2 * MAX_PKT_BURST - 1) + +/* struct ws_mbuf stores the working set of mbufs. It starts with a + prod/cons index to keep track of the number of elemenets. */ +struct ws_mbuf { + struct { + uint16_t prod; + uint16_t cons; + uint16_t nb_rx; + uint16_t pad; /* reserved */ + } idx[MAX_RINGS_PER_TASK]; + struct rte_mbuf *mbuf[][MAX_RING_BURST * 3] __rte_cache_aligned; +}; + +struct port_queue { + uint8_t port; + uint8_t queue; +} __attribute__((packed)); + +struct rx_params_hw { + union { + uint8_t nb_rxports; + uint8_t rxport_mask; + }; + uint8_t last_read_portid; + struct port_queue *rx_pq; +} __attribute__((packed)); + +struct rx_params_hw1 { + struct port_queue rx_pq; +} __attribute__((packed)); + +struct rx_params_sw { + union { + uint8_t nb_rxrings; + uint8_t rxrings_mask; /* Used if rte_is_power_of_2(nb_rxrings)*/ + }; + uint8_t last_read_ring; + struct rte_ring **rx_rings; +} __attribute__((packed)); + +/* If there is only one input ring, the pointer to it can be stored + directly into the task_base instead of having to use a pointer to a + set of rings which would require two dereferences. */ +struct rx_params_sw1 { + struct rte_ring *rx_ring; +} __attribute__((packed)); + +struct tx_params_hw { + uint16_t nb_txports; + struct port_queue *tx_port_queue; +} __attribute__((packed)); + +struct tx_params_sw { + uint16_t nb_txrings; + struct rte_ring **tx_rings; +} __attribute__((packed)); + +struct tx_params_hw_sw { /* Only one port supported in this mode */ + uint16_t nb_txrings; + struct rte_ring **tx_rings; + struct port_queue tx_port_queue; +} __attribute__((packed)); + +struct task_rt_dump { + uint32_t n_print_rx; + uint32_t n_print_tx; + struct input *input; + uint32_t n_trace; + uint32_t cur_trace; + void *pkt_mbuf_addr[MAX_RING_BURST]; /* To track reordering */ + uint8_t pkt_cpy[MAX_RING_BURST][128]; + uint16_t pkt_cpy_len[MAX_RING_BURST]; +}; + +struct task_base; + +#define MAX_RX_PKT_ALL 16384 + +#define MAX_STACKED_RX_FUCTIONS 16 + +typedef uint16_t (*rx_pkt_func) (struct task_base *tbase, struct rte_mbuf ***mbufs); + +struct task_base_aux { + /* Not used when PROX_STATS is not defined */ + struct task_rt_stats stats; + struct task_rt_dump task_rt_dump; + + /* Used if TASK_TSC_RX is enabled*/ + struct { + uint64_t before; + uint64_t after; + } tsc_rx; + + struct rte_mbuf **all_mbufs; + + int rx_prev_count; + int rx_prev_idx; + uint16_t (*rx_pkt_prev[MAX_STACKED_RX_FUCTIONS])(struct task_base *tbase, struct rte_mbuf ***mbufs); + + uint32_t rx_bucket[MAX_RING_BURST + 1]; + uint32_t tx_bucket[MAX_RING_BURST + 1]; + int (*tx_pkt_orig)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + int (*tx_pkt_hw)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + uint16_t (*tx_pkt_try)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts); + void (*stop)(struct task_base *tbase); + void (*start)(struct task_base *tbase); + void (*stop_last)(struct task_base *tbase); + void (*start_first)(struct task_base *tbase); +}; + +/* The task_base is accessed for _all_ task types. In case + no debugging is needed, it has been optimized to fit + into a single cache line to minimize cache pollution */ +struct task_base { + int (*handle_bulk)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts); + int (*tx_pkt)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + uint16_t (*rx_pkt)(struct task_base *tbase, struct rte_mbuf ***mbufs); + + struct task_base_aux* aux; + /* The working set of mbufs contains mbufs that are currently + being handled. */ + struct ws_mbuf *ws_mbuf; + + uint16_t flags; + + union { + struct rx_params_hw rx_params_hw; + struct rx_params_hw1 rx_params_hw1; + struct rx_params_sw rx_params_sw; + struct rx_params_sw1 rx_params_sw1; + }; + + union { + struct tx_params_hw tx_params_hw; + struct tx_params_sw tx_params_sw; + struct tx_params_hw_sw tx_params_hw_sw; + }; +} __attribute__((packed)) __rte_cache_aligned; + +static void task_base_add_rx_pkt_function(struct task_base *tbase, rx_pkt_func to_add) +{ + if (tbase->aux->rx_prev_count == MAX_STACKED_RX_FUCTIONS) { + return; + } + + for (int16_t i = tbase->aux->rx_prev_count; i >= 0; --i) { + tbase->aux->rx_pkt_prev[i + 1] = tbase->aux->rx_pkt_prev[i]; + } + tbase->aux->rx_pkt_prev[0] = tbase->rx_pkt; + tbase->rx_pkt = to_add; + tbase->aux->rx_prev_count++; +} + +static void task_base_del_rx_pkt_function(struct task_base *tbase, rx_pkt_func to_del) +{ + int cur = 0; + int found = 0; + + if (tbase->aux->rx_prev_count == 1) { + tbase->rx_pkt = tbase->aux->rx_pkt_prev[0]; + found = 1; + } else { + for (int16_t i = 0; i < tbase->aux->rx_prev_count; ++i) { + if (found || tbase->aux->rx_pkt_prev[i] != to_del) + tbase->aux->rx_pkt_prev[cur++] = tbase->aux->rx_pkt_prev[i]; + else + found = 1; + } + } + if (found) + tbase->aux->rx_prev_count--; +} + +static rx_pkt_func task_base_get_original_rx_pkt_function(struct task_base *tbase) +{ + if (tbase->aux->rx_prev_count == 0) + return tbase->rx_pkt; + else + return tbase->aux->rx_pkt_prev[tbase->aux->rx_prev_count - 1]; +} + +#endif /* _TASK_BASE_H_ */ diff --git a/VNFs/DPPD-PROX/task_init.c b/VNFs/DPPD-PROX/task_init.c new file mode 100644 index 00000000..6d9c7b3d --- /dev/null +++ b/VNFs/DPPD-PROX/task_init.c @@ -0,0 +1,401 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <string.h> +#include <stdio.h> +#include <rte_version.h> + +#include "prox_port_cfg.h" +#include "prox_malloc.h" +#include "task_init.h" +#include "rx_pkt.h" +#include "tx_pkt.h" +#include "log.h" +#include "quit.h" +#include "lconf.h" +#include "thread_generic.h" +#include "prox_assert.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +static unsigned first_task = 1; +LIST_HEAD(,task_init) head; + +void reg_task(struct task_init* t) +{ + PROX_PANIC(t->handle == NULL, "No handle function specified for task with name %d\n", t->mode); + + if (t->thread_x == NULL) + t->thread_x = thread_generic; + + if (first_task) { + first_task = 0; + LIST_INIT(&head); + } + + LIST_INSERT_HEAD(&head, t, entries); +} + +struct task_init *to_task_init(const char *mode_str, const char *sub_mode_str) +{ + struct task_init *cur_t; + + LIST_FOREACH(cur_t, &head, entries) { + if (!strcmp(mode_str, cur_t->mode_str) && + !strcmp(sub_mode_str, cur_t->sub_mode_str)) { + return cur_t; + } + } + + return NULL; +} + +static int compare_strcmp(const void *a, const void *b) +{ + return strcmp(*(const char * const *)a, *(const char * const *)b); +} + +void tasks_list(void) +{ + struct task_init *cur_t; + char buf[sizeof(cur_t->mode_str) + sizeof(cur_t->sub_mode_str) + 4]; + + int nb_modes = 1; /* master */ + LIST_FOREACH(cur_t, &head, entries) { + ++nb_modes; + } + + char **modes = calloc(nb_modes, sizeof(*modes)); + char **cur_m = modes; + *cur_m++ = strdup("master"); + LIST_FOREACH(cur_t, &head, entries) { + snprintf(buf, sizeof(buf), "%s%s%s", + cur_t->mode_str, + (cur_t->sub_mode_str[0] == 0) ? "" : " / ", + cur_t->sub_mode_str); + *cur_m++ = strdup(buf); + } + qsort(modes, nb_modes, sizeof(*modes), compare_strcmp); + + plog_info("=== List of supported task modes / sub modes ===\n"); + for (cur_m = modes; nb_modes; ++cur_m, --nb_modes) { + plog_info("\t%s\n", *cur_m); + free(*cur_m); + } + free(modes); +} + +static size_t calc_memsize(struct task_args *targ, size_t task_size) +{ + size_t memsize = task_size; + + memsize += sizeof(struct task_base_aux); + + if (targ->nb_rxports != 0) { + memsize += 2 * sizeof(uint8_t)*targ->nb_rxports; + } + if (targ->nb_rxrings != 0 || targ->tx_opt_ring_task) { + memsize += sizeof(struct rte_ring *)*targ->nb_rxrings; + } + if (targ->nb_txrings != 0) { + memsize += sizeof(struct rte_ring *) * targ->nb_txrings; + memsize = RTE_ALIGN_CEIL(memsize, RTE_CACHE_LINE_SIZE); + memsize += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]) * targ->nb_txrings; + } + else if (targ->nb_txports != 0) { + memsize += sizeof(struct port_queue) * targ->nb_txports; + memsize = RTE_ALIGN_CEIL(memsize, RTE_CACHE_LINE_SIZE); + memsize += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]) * targ->nb_txports; + } + else { + memsize = RTE_ALIGN_CEIL(memsize, RTE_CACHE_LINE_SIZE); + memsize += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]); + } + + return memsize; +} + +static void *flush_function(struct task_args *targ) +{ + if (targ->flags & TASK_ARG_DROP) { + return targ->nb_txrings ? flush_queues_sw : flush_queues_hw; + } + else { + return targ->nb_txrings ? flush_queues_no_drop_sw : flush_queues_no_drop_hw; + } +} + +static size_t init_rx_tx_rings_ports(struct task_args *targ, struct task_base *tbase, size_t offset) +{ + if (targ->tx_opt_ring_task) { + tbase->rx_pkt = rx_pkt_self; + } + else if (targ->nb_rxrings != 0) { + + if (targ->nb_rxrings == 1) { + tbase->rx_pkt = rx_pkt_sw1; + tbase->rx_params_sw1.rx_ring = targ->rx_rings[0]; + } + else { + tbase->rx_pkt = rx_pkt_sw; + tbase->rx_params_sw.nb_rxrings = targ->nb_rxrings; + tbase->rx_params_sw.rx_rings = (struct rte_ring **)(((uint8_t *)tbase) + offset); + offset += sizeof(struct rte_ring *)*tbase->rx_params_sw.nb_rxrings; + + for (uint8_t i = 0; i < tbase->rx_params_sw.nb_rxrings; ++i) { + tbase->rx_params_sw.rx_rings[i] = targ->rx_rings[i]; + } + + if (rte_is_power_of_2(targ->nb_rxrings)) { + tbase->rx_pkt = rx_pkt_sw_pow2; + tbase->rx_params_sw.rxrings_mask = targ->nb_rxrings - 1; + } + } + } + else { + if (targ->nb_rxports == 1) { + tbase->rx_pkt = (targ->task_init->flag_features & TASK_FEATURE_MULTI_RX)? rx_pkt_hw1_multi : rx_pkt_hw1; + tbase->rx_params_hw1.rx_pq.port = targ->rx_port_queue[0].port; + tbase->rx_params_hw1.rx_pq.queue = targ->rx_port_queue[0].queue; + } + else { + PROX_ASSERT((targ->nb_rxports != 0) || (targ->task_init->flag_features & TASK_FEATURE_NO_RX)); + tbase->rx_pkt = (targ->task_init->flag_features & TASK_FEATURE_MULTI_RX)? rx_pkt_hw_multi : rx_pkt_hw; + tbase->rx_params_hw.nb_rxports = targ->nb_rxports; + tbase->rx_params_hw.rx_pq = (struct port_queue *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct port_queue) * tbase->rx_params_hw.nb_rxports; + for (int i = 0; i< targ->nb_rxports; i++) { + tbase->rx_params_hw.rx_pq[i].port = targ->rx_port_queue[i].port; + tbase->rx_params_hw.rx_pq[i].queue = targ->rx_port_queue[i].queue; + } + + if (rte_is_power_of_2(targ->nb_rxports)) { + tbase->rx_pkt = (targ->task_init->flag_features & TASK_FEATURE_MULTI_RX)? rx_pkt_hw_pow2_multi : rx_pkt_hw_pow2; + tbase->rx_params_hw.rxport_mask = targ->nb_rxports - 1; + } + } + } + + if ((targ->nb_txrings != 0) && (!targ->tx_opt_ring) && (!(targ->flags & TASK_ARG_DROP))) { + // Transmitting to a ring in NO DROP. We need to make sure the receiving task in not running on the same core. + // Otherwise we might end up in a dead lock: trying in a loop to transmit to a task which cannot receive anymore + // (as npt being scheduled). + struct core_task ct; + struct task_args *dtarg; + for (unsigned int j = 0; j < targ->nb_txrings; j++) { + ct = targ->core_task_set[0].core_task[j]; + PROX_PANIC(ct.core == targ->lconf->id, "Core %d, task %d: NO_DROP task transmitting to another task (core %d, task %d) running on on same core => potential deadlock\n", targ->lconf->id, targ->id, ct.core, ct.task); + //plog_info("Core %d, task %d: NO_DROP task transmitting to another task (core %d, task %d) running on on same core => potential deadlock\n", targ->lconf->id, targ->id, ct.core, ct.task); + } + } + if ((targ->nb_txrings != 0) && (targ->nb_txports == 1)) { + /* Transmitting to multiple rings and one port */ + plog_info("Initializing with 1 port %d queue %d nb_rings=%d\n", targ->tx_port_queue[0].port, targ->tx_port_queue[0].queue, targ->nb_txrings); + tbase->tx_params_hw_sw.tx_port_queue.port = targ->tx_port_queue[0].port; + tbase->tx_params_hw_sw.tx_port_queue.queue = targ->tx_port_queue[0].queue; + if (!targ->tx_opt_ring) { + tbase->tx_params_hw_sw.nb_txrings = targ->nb_txrings; + tbase->tx_params_hw_sw.tx_rings = (struct rte_ring **)(((uint8_t *)tbase) + offset); + offset += sizeof(struct rte_ring *)*tbase->tx_params_hw_sw.nb_txrings; + + for (uint8_t i = 0; i < tbase->tx_params_hw_sw.nb_txrings; ++i) { + tbase->tx_params_hw_sw.tx_rings[i] = targ->tx_rings[i]; + } + + offset = RTE_ALIGN_CEIL(offset, RTE_CACHE_LINE_SIZE); + tbase->ws_mbuf = (struct ws_mbuf *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]) * tbase->tx_params_hw_sw.nb_txrings; + } + } + else if (!targ->tx_opt_ring) { + if (targ->nb_txrings != 0) { + tbase->tx_params_sw.nb_txrings = targ->nb_txrings; + tbase->tx_params_sw.tx_rings = (struct rte_ring **)(((uint8_t *)tbase) + offset); + offset += sizeof(struct rte_ring *)*tbase->tx_params_sw.nb_txrings; + + for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) { + tbase->tx_params_sw.tx_rings[i] = targ->tx_rings[i]; + } + + offset = RTE_ALIGN_CEIL(offset, RTE_CACHE_LINE_SIZE); + tbase->ws_mbuf = (struct ws_mbuf *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]) * tbase->tx_params_sw.nb_txrings; + } + else if (targ->nb_txports != 0) { + tbase->tx_params_hw.nb_txports = targ->nb_txports; + tbase->tx_params_hw.tx_port_queue = (struct port_queue *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct port_queue) * tbase->tx_params_hw.nb_txports; + for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) { + tbase->tx_params_hw.tx_port_queue[i].port = targ->tx_port_queue[i].port; + tbase->tx_params_hw.tx_port_queue[i].queue = targ->tx_port_queue[i].queue; + } + + offset = RTE_ALIGN_CEIL(offset, RTE_CACHE_LINE_SIZE); + tbase->ws_mbuf = (struct ws_mbuf *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]) * tbase->tx_params_hw.nb_txports; + } + else { + offset = RTE_ALIGN_CEIL(offset, RTE_CACHE_LINE_SIZE); + tbase->ws_mbuf = (struct ws_mbuf *)(((uint8_t *)tbase) + offset); + offset += sizeof(struct ws_mbuf) + sizeof(((struct ws_mbuf*)0)->mbuf[0]); + } + + struct ws_mbuf* w = tbase->ws_mbuf; + struct task_args *prev = targ->tx_opt_ring_task; + + while (prev) { + prev->tbase->ws_mbuf = w; + prev = prev->tx_opt_ring_task; + } + } + if (targ->nb_txrings == 1 || targ->nb_txports == 1 || targ->tx_opt_ring) { + if (targ->task_init->flag_features & TASK_FEATURE_NEVER_DISCARDS) { + if (targ->tx_opt_ring) { + tbase->tx_pkt = tx_pkt_never_discard_self; + } + else if (targ->flags & TASK_ARG_DROP) { + if (targ->task_init->flag_features & TASK_FEATURE_THROUGHPUT_OPT) + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_never_discard_sw1 : tx_pkt_never_discard_hw1_thrpt_opt; + else + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_never_discard_sw1 : tx_pkt_never_discard_hw1_lat_opt; + } + else { + if (targ->task_init->flag_features & TASK_FEATURE_THROUGHPUT_OPT) + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_no_drop_never_discard_sw1 : tx_pkt_no_drop_never_discard_hw1_thrpt_opt; + else + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_no_drop_never_discard_sw1 : tx_pkt_no_drop_never_discard_hw1_lat_opt; + } + if ((targ->nb_txrings) || ((targ->task_init->flag_features & TASK_FEATURE_THROUGHPUT_OPT) == 0)) + tbase->flags |= FLAG_NEVER_FLUSH; + else + targ->lconf->flush_queues[targ->task] = flush_function(targ); + } + else { + if (targ->tx_opt_ring) { + tbase->tx_pkt = tx_pkt_self; + } + else if (targ->flags & TASK_ARG_DROP) { + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_sw1 : tx_pkt_hw1; + } + else { + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_no_drop_sw1 : tx_pkt_no_drop_hw1; + } + tbase->flags |= FLAG_NEVER_FLUSH; + } + } + else { + if (targ->flags & TASK_ARG_DROP) { + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_sw : tx_pkt_hw; + } + else { + tbase->tx_pkt = targ->nb_txrings ? tx_pkt_no_drop_sw : tx_pkt_no_drop_hw; + } + + targ->lconf->flush_queues[targ->task] = flush_function(targ); + } + + if (targ->task_init->flag_features & TASK_FEATURE_NO_RX) { + tbase->rx_pkt = rx_pkt_dummy; + } + + if (targ->nb_txrings == 0 && targ->nb_txports == 0) { + tbase->tx_pkt = tx_pkt_drop_all; + } + + return offset; +} + +struct task_base *init_task_struct(struct task_args *targ) +{ + struct task_init* t = targ->task_init; + size_t offset = 0; + size_t memsize = calc_memsize(targ, t->size); + uint8_t task_socket = rte_lcore_to_socket_id(targ->lconf->id); + struct task_base *tbase = prox_zmalloc(memsize, task_socket); + PROX_PANIC(tbase == NULL, "Failed to allocate memory for task (%zu bytes)", memsize); + offset += t->size; + + if (targ->nb_txrings == 0 && targ->nb_txports == 0) + tbase->flags |= FLAG_NEVER_FLUSH; + + offset = init_rx_tx_rings_ports(targ, tbase, offset); + tbase->aux = (struct task_base_aux *)(((uint8_t *)tbase) + offset); + + if (targ->task_init->flag_features & TASK_FEATURE_RX_ALL) { + task_base_add_rx_pkt_function(tbase, rx_pkt_all); + tbase->aux->all_mbufs = prox_zmalloc(MAX_RX_PKT_ALL * sizeof(* tbase->aux->all_mbufs), task_socket); + } + if (targ->task_init->flag_features & TASK_FEATURE_TSC_RX) { + task_base_add_rx_pkt_function(tbase, rx_pkt_tsc); + } + + offset += sizeof(struct task_base_aux); + + tbase->handle_bulk = t->handle; + + targ->tbase = tbase; + if (t->init) { + t->init(tbase, targ); + } + tbase->aux->start = t->start; + tbase->aux->stop = t->stop; + tbase->aux->start_first = t->start_first; + tbase->aux->stop_last = t->stop_last; + if ((targ->nb_txrings != 0) && (targ->nb_txports == 1)) { + tbase->aux->tx_pkt_hw = tx_pkt_no_drop_never_discard_hw1_no_pointer; + } + if (targ->tx_opt_ring) { + tbase->aux->tx_pkt_try = tx_try_self; + } else if (targ->nb_txrings == 1) { + tbase->aux->tx_pkt_try = tx_try_sw1; + } else if (targ->nb_txports) { + tbase->aux->tx_pkt_try = tx_try_hw1; + } + + return tbase; +} + +struct task_args *find_reachable_task_sending_to_port(struct task_args *from) +{ + if (!from->nb_txrings) + return from; + + struct core_task ct; + struct task_args *dtarg, *ret; + + for (uint32_t i = 0; i < from->nb_txrings; ++i) { + ct = from->core_task_set[0].core_task[i]; + dtarg = core_targ_get(ct.core, ct.task); + ret = find_reachable_task_sending_to_port(dtarg); + if (ret) + return ret; + } + return NULL; +} + +struct prox_port_cfg *find_reachable_port(struct task_args *from) +{ + struct task_args *dst = find_reachable_task_sending_to_port(from); + + if (dst) { + int port_id = dst->tx_port_queue[0].port; + + return &prox_port_cfg[port_id]; + } + return NULL; +} diff --git a/VNFs/DPPD-PROX/task_init.h b/VNFs/DPPD-PROX/task_init.h new file mode 100644 index 00000000..beb4de02 --- /dev/null +++ b/VNFs/DPPD-PROX/task_init.h @@ -0,0 +1,239 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TASK_INIT_H_ +#define _TASK_INIT_H_ + +#include <sys/queue.h> + +#include <rte_common.h> +#include <rte_sched.h> +#include <rte_ether.h> +#include "task_base.h" +#include "prox_globals.h" +#include "ip6_addr.h" +#include "flow_iter.h" +#include "parse_utils.h" + +struct rte_mbuf; +struct lcore_cfg; + +#if MAX_RINGS_PER_TASK < PROX_MAX_PORTS +#error MAX_RINGS_PER_TASK < PROX_MAX_PORTS +#endif + +#define TASK_ARG_DROP 0x01 +#define TASK_ARG_RX_RING 0x02 +#define TASK_ARG_RTE_TABLE 0x08 +#define TASK_ARG_LOCAL_LPM 0x10 +#define TASK_ARG_QINQ_ACL 0x20 +#define TASK_ARG_CTRL_RINGS_P 0x40 +#define TASK_ARG_DST_MAC_SET 0x80 +#define TASK_ARG_SRC_MAC_SET 0x100 +#define TASK_ARG_DO_NOT_SET_SRC_MAC 0x200 +#define TASK_ARG_DO_NOT_SET_DST_MAC 0x400 +#define TASK_ARG_HW_SRC_MAC 0x800 + +enum protocols {IPV4, ARP, IPV6}; + +struct qos_cfg { + struct rte_sched_port_params port_params; + struct rte_sched_subport_params subport_params[1]; + struct rte_sched_pipe_params pipe_params[1]; +}; + +enum task_mode {NOT_SET, MASTER, QINQ_DECAP4, QINQ_DECAP6, + QINQ_ENCAP4, QINQ_ENCAP6, GRE_DECAP, GRE_ENCAP,CGNAT, +}; + +struct task_args; + +struct task_init { + enum task_mode mode; + char mode_str[32]; + char sub_mode_str[32]; + void (*early_init)(struct task_args *targ); + void (*init)(struct task_base *tbase, struct task_args *targ); + int (*handle)(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts); + void (*start)(struct task_base *tbase); + void (*stop)(struct task_base *tbase); + void (*start_first)(struct task_base *tbase); + void (*stop_last)(struct task_base *tbase); + int (*thread_x)(struct lcore_cfg* lconf); + struct flow_iter flow_iter; + size_t size; + uint16_t flag_req_data; /* flags from prox_shared.h */ + uint64_t flag_features; + uint32_t mbuf_size; + LIST_ENTRY(task_init) entries; +}; + +static int task_init_flag_set(struct task_init *task_init, uint64_t flag) +{ + return !!(task_init->flag_features & flag); +} + +enum police_action { + ACT_GREEN = e_RTE_METER_GREEN, + ACT_YELLOW = e_RTE_METER_YELLOW, + ACT_RED = e_RTE_METER_RED, + ACT_DROP = 3, + ACT_INVALID = 4 +}; + +/* Configuration for task that is only used during startup. */ +struct task_args { + struct task_base *tbase; + struct task_init* task_init; + struct rte_mempool *pool; + char pool_name[MAX_NAME_SIZE]; + struct lcore_cfg *lconf; + uint32_t nb_mbuf; + uint32_t mbuf_size; + uint8_t mbuf_size_set_explicitely; + uint32_t nb_cache_mbuf; + uint8_t nb_slave_threads; + uint8_t nb_worker_threads; + uint8_t worker_thread_id; + uint8_t task; + uint32_t id; + struct core_task_set core_task_set[MAX_PROTOCOLS]; + struct task_args *prev_tasks[MAX_RINGS_PER_TASK]; + uint32_t n_prev_tasks; + uint32_t ring_size; /* default is RX_RING_SIZE */ + struct qos_cfg qos_conf; + uint32_t flags; + uint32_t runtime_flags; + uint8_t nb_txports; + uint8_t nb_txrings; + uint8_t nb_rxrings; + uint8_t tot_rxrings; + uint8_t nb_rxports; + uint32_t byte_offset; + uint32_t gateway_ipv4; + uint32_t number_gen_ip; + uint32_t local_ipv4; + struct ipv6_addr local_ipv6; /* For IPv6 Tunnel, it's the local tunnel endpoint address */ + struct rte_ring *rx_rings[MAX_RINGS_PER_TASK]; + struct rte_ring *tx_rings[MAX_RINGS_PER_TASK]; + uint32_t tot_n_txrings_inited; + struct ether_addr edaddr; + struct ether_addr esaddr; + struct port_queue tx_port_queue[PROX_MAX_PORTS]; + struct port_queue rx_port_queue[PROX_MAX_PORTS]; + /* Used to set up actual task at initialization time. */ + enum task_mode mode; + /* Destination output position in hw or sw when using mac learned dest port. */ + uint8_t mapping[PROX_MAX_PORTS]; + struct rte_table_hash *cpe_table; + struct rte_table_hash *qinq_gre_table; + struct rte_hash *cpe_gre_hash; + struct rte_hash *qinq_gre_hash; + struct cpe_data *cpe_data; + struct cpe_gre_data *cpe_gre_data; + struct qinq_gre_data *qinq_gre_data; + uint8_t tx_opt_ring; + struct task_args *tx_opt_ring_task; + uint32_t qinq_tag; + +#ifdef ENABLE_EXTRA_USER_STATISTICS + uint32_t n_users; // Number of users in user table. +#endif + uint32_t n_flows; // Number of flows used in policing + uint32_t cir; + uint32_t cbs; + uint32_t ebs; + uint32_t pir; + uint32_t pbs; + uint32_t overhead; + enum police_action police_act[3][3]; + uint32_t marking[4]; + uint32_t n_max_rules; + uint32_t random_delay_us; + uint32_t delay_us; + uint32_t cpe_table_timeout_ms; + uint32_t etype; +#ifdef GRE_TP + uint32_t tb_rate; /**< Pipe token bucket rate (measured in bytes per second) */ + uint32_t tb_size; /**< Pipe token bucket size (measured in credits) */ +#endif + uint8_t tunnel_hop_limit; /* IPv6 Tunnel - Hop limit */ + uint16_t lookup_port_mask; /* Ipv6 Tunnel - Mask applied to UDP/TCP port before lookup */ + uint32_t ctrl_freq; + uint8_t lb_friend_core; + uint8_t lb_friend_task; + /* gen related*/ + uint64_t rate_bps; + uint32_t n_rand_str; + char rand_str[64][64]; + uint32_t rand_offset[64]; + char pcap_file[256]; + uint32_t accur_pos; + uint32_t sig_pos; + uint32_t sig; + uint32_t lat_pos; + uint32_t packet_id_pos; + uint32_t latency_buffer_size; + uint32_t bucket_size; + uint32_t lat_enabled; + uint32_t pkt_size; + uint8_t pkt_inline[ETHER_MAX_LEN]; + uint32_t probability; + char nat_table[256]; + uint32_t use_src; + char route_table[256]; + char rules[256]; + char dscp[256]; + char tun_bindings[256]; + char cpe_table_name[256]; + char user_table[256]; + uint32_t n_concur_conn; + char streams[256]; + uint32_t min_bulk_size; + uint32_t max_bulk_size; + uint32_t max_setup_rate; + uint32_t n_pkts; + uint32_t loop; + uint32_t flow_table_size; + char dpi_engine_path[256]; + char dpi_engine_args[16][256]; + uint32_t n_dpi_engine_args; + uint32_t generator_id; + uint32_t accuracy_limit_nsec; + /* cgnat related */ + uint32_t public_ip_count; + struct public_ip_config_info *public_ip_config_info; + struct public_entry *public_entries; + struct private_flow_entry *private_flow_entries; + struct rte_hash *public_ip_port_hash; + struct rte_hash *private_ip_port_hash; + struct rte_hash *private_ip_hash; + struct private_ip_info *private_ip_info; +}; + +/* Return the first port that is reachable through the task. If the + task itself does not send directly to a port, the function will + search reachable tasks through each outgoing ring */ +struct task_args *find_reachable_task_sending_to_port(struct task_args *from); +struct prox_port_cfg *find_reachable_port(struct task_args *from); + +struct task_base *init_task_struct(struct task_args *targ); +struct task_init *to_task_init(const char *mode_str, const char *sub_mode_str); +void tasks_list(void); + +void reg_task(struct task_init* t); + +#endif /* _TASK_INIT_H_ */ diff --git a/VNFs/DPPD-PROX/thread_generic.c b/VNFs/DPPD-PROX/thread_generic.c new file mode 100644 index 00000000..f596bf25 --- /dev/null +++ b/VNFs/DPPD-PROX/thread_generic.c @@ -0,0 +1,196 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_table_hash.h> + +#include "log.h" +#include "thread_generic.h" +#include "stats.h" +#include "tx_pkt.h" +#include "lconf.h" +#include "hash_entry_types.h" +#include "defines.h" +#include "hash_utils.h" + +struct tsc_task { + uint64_t tsc; + uint64_t (* tsc_task)(struct lcore_cfg *lconf); +}; + +static uint64_t tsc_drain(struct lcore_cfg *lconf) +{ + lconf_flush_all_queues(lconf); + return DRAIN_TIMEOUT; +} + +static uint64_t tsc_term(struct lcore_cfg *lconf) +{ + if (lconf_is_req(lconf) && lconf_do_flags(lconf)) { + lconf_flush_all_queues(lconf); + return -2; + } + return TERM_TIMEOUT; +} + +static uint64_t tsc_period(struct lcore_cfg *lconf) +{ + lconf->period_func(lconf->period_data); + return lconf->period_timeout; +} + +static uint64_t tsc_ctrl(struct lcore_cfg *lconf) +{ + const uint8_t n_tasks_all = lconf->n_tasks_all; + void *msgs[MAX_RING_BURST]; + uint16_t n_msgs; + + for (uint8_t task_id = 0; task_id < n_tasks_all; ++task_id) { + if (lconf->ctrl_rings_m[task_id] && lconf->ctrl_func_m[task_id]) { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + n_msgs = rte_ring_sc_dequeue_burst(lconf->ctrl_rings_m[task_id], msgs, MAX_RING_BURST); +#else + n_msgs = rte_ring_sc_dequeue_burst(lconf->ctrl_rings_m[task_id], msgs, MAX_RING_BURST, NULL); +#endif + if (n_msgs) { + lconf->ctrl_func_m[task_id](lconf->tasks_all[task_id], msgs, n_msgs); + } + } + if (lconf->ctrl_rings_p[task_id] && lconf->ctrl_func_p[task_id]) { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + n_msgs = rte_ring_sc_dequeue_burst(lconf->ctrl_rings_p[task_id], msgs, MAX_RING_BURST); +#else + n_msgs = rte_ring_sc_dequeue_burst(lconf->ctrl_rings_p[task_id], msgs, MAX_RING_BURST, NULL); +#endif + if (n_msgs) { + lconf->ctrl_func_p[task_id](lconf->tasks_all[task_id], (struct rte_mbuf **)msgs, n_msgs); + } + } + } + return lconf->ctrl_timeout; +} + +int thread_generic(struct lcore_cfg *lconf) +{ + struct task_base *tasks[MAX_TASKS_PER_CORE]; + int next[MAX_TASKS_PER_CORE] = {0}; + struct rte_mbuf **mbufs; + uint64_t cur_tsc = rte_rdtsc(); + uint8_t zero_rx[MAX_TASKS_PER_CORE] = {0}; + struct tsc_task tsc_tasks[] = { + {.tsc = cur_tsc, .tsc_task = tsc_term}, + {.tsc = cur_tsc + DRAIN_TIMEOUT, .tsc_task = tsc_drain}, + {.tsc = -1}, + {.tsc = -1}, + {.tsc = -1}, + }; + uint8_t n_tasks_run = lconf->n_tasks_run; + + if (lconf->period_func) { + tsc_tasks[2].tsc = cur_tsc + lconf->period_timeout; + tsc_tasks[2].tsc_task = tsc_period; + } + + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + if (lconf->ctrl_func_m[task_id]) { + tsc_tasks[3].tsc = cur_tsc + lconf->ctrl_timeout; + tsc_tasks[3].tsc_task = tsc_ctrl; + break; + } + if (lconf->ctrl_func_p[task_id]) { + tsc_tasks[3].tsc = cur_tsc + lconf->ctrl_timeout; + tsc_tasks[3].tsc_task = tsc_ctrl; + break; + } + } + + /* sort tsc tasks */ + for (size_t i = 0; i < sizeof(tsc_tasks)/sizeof(tsc_tasks[0]); ++i) { + for (size_t j = i + 1; j < sizeof(tsc_tasks)/sizeof(tsc_tasks[0]); ++j) { + if (tsc_tasks[i].tsc > tsc_tasks[j].tsc) { + struct tsc_task tmp = tsc_tasks[i]; + tsc_tasks[i] = tsc_tasks[j]; + tsc_tasks[j] = tmp; + } + } + } + struct tsc_task next_tsc = tsc_tasks[0]; + + for (;;) { + cur_tsc = rte_rdtsc(); + /* Sort scheduled tsc_tasks starting from earliest + first. A linear search is performed moving + tsc_tasks that are scheduled earlier to the front + of the list. There is a high frequency tsc_task in + most cases. As a consequence, the currently + scheduled tsc_task will be rescheduled to be + executed as the first again. If many tsc_tasks are + to be used, the algorithm should be replaced with a + priority-queue (heap). */ + if (unlikely(cur_tsc >= next_tsc.tsc)) { + uint64_t resched_diff = tsc_tasks[0].tsc_task(lconf); + + if (resched_diff == (uint64_t)-2) { + n_tasks_run = lconf->n_tasks_run; + if (!n_tasks_run) + return 0; + for (int i = 0; i < lconf->n_tasks_run; ++i) { + tasks[i] = lconf->tasks_run[i]; + + uint8_t task_id = lconf_get_task_id(lconf, tasks[i]); + if (lconf->targs[task_id].task_init->flag_features & TASK_FEATURE_ZERO_RX) + zero_rx[i] = 1; + } + } + + uint64_t new_tsc = tsc_tasks[0].tsc + resched_diff; + tsc_tasks[0].tsc = new_tsc; + next_tsc.tsc = new_tsc; + + for (size_t i = 1; i < sizeof(tsc_tasks)/sizeof(tsc_tasks[0]); ++i) { + if (new_tsc < tsc_tasks[i].tsc) { + if (i > 1) { + tsc_tasks[i - 1] = next_tsc; + next_tsc = tsc_tasks[0]; + } + break; + } + else + tsc_tasks[i - 1] = tsc_tasks[i]; + } + } + + uint16_t nb_rx; + for (uint8_t task_id = 0; task_id < n_tasks_run; ++task_id) { + struct task_base *t = tasks[task_id]; + struct task_args *targ = &lconf->targs[task_id]; + // Do not skip a task receiving packets from an optimized ring + // as the transmitting task expects such a receiving task to always run and consume + // the transmitted packets. + if (unlikely(next[task_id] && (targ->tx_opt_ring_task == NULL))) { + // plogx_info("task %d is too busy\n", task_id); + next[task_id] = 0; + } else { + nb_rx = t->rx_pkt(t, &mbufs); + if (likely(nb_rx || zero_rx[task_id])) { + next[task_id] = t->handle_bulk(t, mbufs, nb_rx); + } + } + + } + } + return 0; +} diff --git a/VNFs/DPPD-PROX/thread_generic.h b/VNFs/DPPD-PROX/thread_generic.h new file mode 100644 index 00000000..a5b45a18 --- /dev/null +++ b/VNFs/DPPD-PROX/thread_generic.h @@ -0,0 +1,30 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _THREAD_GENERIC_H_ +#define _THREAD_GENERIC_H_ + +struct lcore_cfg; + +/* The generic thread can do everything needed for each of the tasks. + It is not optimized for any specific case and suggested use is only + for testing purpose and for tasks that require to run a function + periodically (i.e. ARP management). More specific "thread_XXX" + functions should be used to only do the steps only necessary for + the task. */ +int thread_generic(struct lcore_cfg *lconf); + +#endif /* _THREAD_GENERIC_H_ */ diff --git a/VNFs/DPPD-PROX/thread_nop.c b/VNFs/DPPD-PROX/thread_nop.c new file mode 100644 index 00000000..ba30dc61 --- /dev/null +++ b/VNFs/DPPD-PROX/thread_nop.c @@ -0,0 +1,66 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> + +#include "log.h" +#include "lconf.h" +#include "thread_nop.h" +#include "handle_nop.h" +#include "stats.h" +#include "lconf.h" +#include "defines.h" + +int thread_nop(struct lcore_cfg *lconf) +{ + struct task_base *tasks[MAX_TASKS_PER_CORE]; + struct rte_mbuf **mbufs; + uint64_t cur_tsc = rte_rdtsc(); + uint64_t term_tsc = cur_tsc; + uint64_t drain_tsc = cur_tsc; + uint8_t n_tasks_run = 0; + + for (;;) { + cur_tsc = rte_rdtsc(); + if (cur_tsc > term_tsc) { + term_tsc = cur_tsc + TERM_TIMEOUT; + if (lconf_is_req(lconf) && lconf_do_flags(lconf)) { + n_tasks_run = lconf->n_tasks_run; + + if (!n_tasks_run) + return 0; + for (int i = 0; i < lconf->n_tasks_run; ++i) { + tasks[i] = lconf->tasks_run[i]; + } + } + } + if (cur_tsc > drain_tsc) { + drain_tsc = cur_tsc + DRAIN_TIMEOUT; + lconf_flush_all_queues(lconf); + } + + for (uint8_t task_id = 0; task_id < n_tasks_run; ++task_id) { + struct task_base *t = tasks[task_id]; + uint16_t nb_rx = t->rx_pkt(t, &mbufs); + + if (likely(nb_rx)) { + handle_nop_bulk(t, mbufs, nb_rx); + } + } + } + + return 0; +} diff --git a/VNFs/DPPD-PROX/thread_nop.h b/VNFs/DPPD-PROX/thread_nop.h new file mode 100644 index 00000000..6bc4465a --- /dev/null +++ b/VNFs/DPPD-PROX/thread_nop.h @@ -0,0 +1,28 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _THREAD_NOP_H_ +#define _THREAD_NOP_H_ + +struct lcore_cfg; + +/* A separate threading function specifically with minimal features is + supplied to allow testing with minimal overhead. This thread + function is only used when all tasks on the core use have the + .thread_x field set to thread_nop. */ +int thread_nop(struct lcore_cfg *lconf); + +#endif /* _THREAD_NOP_H_ */ diff --git a/VNFs/DPPD-PROX/thread_pipeline.c b/VNFs/DPPD-PROX/thread_pipeline.c new file mode 100644 index 00000000..242b137b --- /dev/null +++ b/VNFs/DPPD-PROX/thread_pipeline.c @@ -0,0 +1,295 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_cycles.h> +#include <rte_port_ethdev.h> +#include <rte_port_ring.h> +#include <rte_version.h> + +#include "log.h" +#include "quit.h" +#include "thread_pipeline.h" +#include "lconf.h" +#include "defines.h" + +/* Helper function: create pipeline, input ports and output ports */ +void init_pipe_create_in_out(struct task_pipe *tpipe, struct task_args *targ) +{ + struct task_base *tbase = (struct task_base *)tpipe; + const char *name = targ->lconf->name; + const char *mode = targ->task_init->mode_str; + uint8_t lcore_id = targ->lconf->id; + uint8_t task_id = targ->task; + int err; + + /* create pipeline */ + struct rte_pipeline_params pipeline_params = { + .name = name, + .socket_id = rte_lcore_to_socket_id(lcore_id), + }; + tpipe->p = rte_pipeline_create(&pipeline_params); + PROX_PANIC(tpipe->p == NULL, + "Failed to create %s pipeline on core %u task %u\n", + mode, lcore_id, task_id); + + /* create pipeline input ports */ + if (targ->nb_rxrings != 0) { + for (uint8_t i = 0; i < tbase->rx_params_sw.nb_rxrings; ++i) { + struct rte_port_ring_reader_params port_ring_params = { + .ring = tbase->rx_params_sw.rx_rings[i], + }; + struct rte_pipeline_port_in_params port_params = { + .ops = &rte_port_ring_reader_ops, + .arg_create = &port_ring_params, + .f_action = NULL, //TODO: fill metadata + .arg_ah = NULL, + .burst_size = MAX_RING_BURST, + }; + err = rte_pipeline_port_in_create(tpipe->p, + &port_params, &tpipe->port_in_id[i]); + PROX_PANIC(err != 0, "Failed to create SW input port %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, mode, lcore_id, task_id, err); + } + tpipe->n_ports_in = tbase->rx_params_sw.nb_rxrings; + } + else { + for (uint8_t i = 0; i < tbase->rx_params_hw.nb_rxports; ++i) { + struct rte_port_ethdev_reader_params port_ethdev_params = { + .port_id = tbase->rx_params_hw.rx_pq[i].port, + .queue_id = tbase->rx_params_hw.rx_pq[i].queue, + }; + struct rte_pipeline_port_in_params port_params = { + .ops = &rte_port_ethdev_reader_ops, + .arg_create = &port_ethdev_params, + .f_action = NULL, //TODO: fill metadata + .arg_ah = NULL, + .burst_size = MAX_PKT_BURST, + }; + err = rte_pipeline_port_in_create(tpipe->p, + &port_params, &tpipe->port_in_id[0]); + PROX_PANIC(err != 0, "Failed to create HW input port " + "for %s pipeline on core %u task %u: " + "err = %d\n", + mode, lcore_id, task_id, err); + } + tpipe->n_ports_in = tbase->rx_params_hw.nb_rxports; + } + PROX_PANIC(tpipe->n_ports_in < 1, "No input port created " + "for %s pipeline on core %u task %u\n", + mode, lcore_id, task_id); + + /* create pipeline output ports */ + if (targ->nb_txrings != 0) { + for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) { + struct rte_port_ring_writer_params port_ring_params = { + .ring = tbase->tx_params_sw.tx_rings[i], + .tx_burst_sz = MAX_RING_BURST, + }; + struct rte_pipeline_port_out_params port_params = { + .ops = &rte_port_ring_writer_ops, + .arg_create = &port_ring_params, + .f_action = NULL, //TODO +#if RTE_VERSION < RTE_VERSION_NUM(16,4,0,0) + .f_action_bulk = NULL, //TODO +#endif + .arg_ah = NULL, + }; + err = rte_pipeline_port_out_create(tpipe->p, + &port_params, &tpipe->port_out_id[i]); + PROX_PANIC(err != 0, "Failed to create SW output port %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, mode, lcore_id, task_id, err); + } + tpipe->n_ports_out = tbase->tx_params_sw.nb_txrings; + } + else { + for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) { + struct rte_port_ethdev_writer_params port_ethdev_params = { + .port_id = tbase->tx_params_hw.tx_port_queue[i].port, + .queue_id = tbase->tx_params_hw.tx_port_queue[i].queue, + .tx_burst_sz = MAX_PKT_BURST, + }; + struct rte_pipeline_port_out_params port_params = { + .ops = &rte_port_ethdev_writer_ops, + .arg_create = &port_ethdev_params, + .f_action = NULL, //TODO +#if RTE_VERSION < RTE_VERSION_NUM(16,4,0,0) + .f_action_bulk = NULL, //TODO +#endif + .arg_ah = NULL, + }; + err = rte_pipeline_port_out_create(tpipe->p, + &port_params, &tpipe->port_out_id[i]); + PROX_PANIC(err != 0, "Failed to create HW output port %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, mode, lcore_id, task_id, err); + } + tpipe->n_ports_out = tbase->tx_params_hw.nb_txports; + } + PROX_PANIC(tpipe->n_ports_out < 1, "No output port created " + "for %s pipeline on core %u task %u\n", + mode, lcore_id, task_id); +} + +/* Helper function: connect pipeline input ports to one pipeline table */ +void init_pipe_connect_one(struct task_pipe *tpipe, struct task_args *targ, + uint32_t table_id) +{ + const char *mode = targ->task_init->mode_str; + uint8_t lcore_id = targ->lconf->id; + uint8_t task_id = targ->task; + int err; + + for (uint8_t i = 0; i < tpipe->n_ports_in; ++i) { + err = rte_pipeline_port_in_connect_to_table(tpipe->p, + tpipe->port_in_id[i], table_id); + PROX_PANIC(err != 0, "Failed to connect input port %u to table id %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, table_id, mode, lcore_id, task_id, err); + } +} + +/* Helper function: connect pipeline input ports to all pipeline tables */ +void init_pipe_connect_all(struct task_pipe *tpipe, struct task_args *targ) +{ + const char *mode = targ->task_init->mode_str; + uint8_t lcore_id = targ->lconf->id; + uint8_t task_id = targ->task; + int err; + + PROX_PANIC(tpipe->n_tables < tpipe->n_ports_in, + "Not enough tables (%u) to connect %u input ports " + "for %s pipeline on core %u task %u\n", + tpipe->n_tables, tpipe->n_ports_in, + mode, lcore_id, task_id); + + for (uint8_t i = 0; i < tpipe->n_ports_in; ++i) { + err = rte_pipeline_port_in_connect_to_table(tpipe->p, + tpipe->port_in_id[i], tpipe->table_id[i]); + PROX_PANIC(err != 0, "Failed to connect input port %u to table id %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, tpipe->table_id[i], mode, lcore_id, task_id, err); + } +} + +/* Helper function: enable pipeline input ports */ +void init_pipe_enable(struct task_pipe *tpipe, struct task_args *targ) +{ + const char *mode = targ->task_init->mode_str; + uint8_t lcore_id = targ->lconf->id; + uint8_t task_id = targ->task; + int err; + + for (uint8_t i = 0; i < tpipe->n_ports_in; ++i) { + err = rte_pipeline_port_in_enable(tpipe->p, tpipe->port_in_id[i]); + PROX_PANIC(err != 0, "Failed to enable input port %u " + "for %s pipeline on core %u task %u: " + "err = %d\n", + i, mode, lcore_id, task_id, err); + } +} + +/* Helper function: check pipeline consistency */ +void init_pipe_check(struct task_pipe *tpipe, struct task_args *targ) +{ + const char *mode = targ->task_init->mode_str; + uint8_t lcore_id = targ->lconf->id; + uint8_t task_id = targ->task; + int err; + + err = rte_pipeline_check(tpipe->p); + PROX_PANIC(err != 0, "Failed consistency check " + "for %s pipeline on core %u task %u: " + "err = %d\n", + mode, lcore_id, task_id, err); +} + +/* This function will panic on purpose: tasks based on Packet Framework + pipelines should not be invoked via the usual task_base.handle_bulk method */ +int handle_pipe(struct task_base *tbase, + __attribute__((unused)) struct rte_mbuf **mbufs, + __attribute__((unused)) uint16_t n_pkts) +{ + uint32_t lcore_id = rte_lcore_id(); + struct lcore_cfg *lconf = &lcore_cfg[lcore_id]; + + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + struct task_args *targ = &lconf->targs[task_id]; + if (lconf->tasks_all[task_id] == tbase) { + PROX_PANIC(1, "Error on core %u task %u: cannot run " + "%s pipeline and other non-PF tasks\n", + lcore_id, task_id, targ->task_init->mode_str); + } + } + PROX_PANIC(1, "Error: cannot find task on core %u\n", lcore_id); + return 0; +} + +int thread_pipeline(struct lcore_cfg *lconf) +{ + struct task_pipe *pipes[MAX_TASKS_PER_CORE]; + uint64_t cur_tsc = rte_rdtsc(); + uint64_t term_tsc = cur_tsc + TERM_TIMEOUT; + uint64_t drain_tsc = cur_tsc + DRAIN_TIMEOUT; + const uint8_t nb_tasks = lconf->n_tasks_all; + + for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { + //TODO: solve other mutually exclusive thread/tasks + struct task_args *targ = &lconf->targs[task_id]; + PROX_PANIC(targ->task_init->thread_x != thread_pipeline, + "Invalid task %u '%s' on core %u: %s() can only " + "run tasks based on Packet Framework pipelines\n", + targ->task, targ->task_init->mode_str, + targ->lconf->id, __func__); + + pipes[task_id] = (struct task_pipe *)lconf->tasks_all[task_id]; + } + + lconf->flags |= LCONF_FLAG_RUNNING; + for (;;) { + cur_tsc = rte_rdtsc(); + if (cur_tsc > drain_tsc) { + drain_tsc = cur_tsc + DRAIN_TIMEOUT; + + if (cur_tsc > term_tsc) { + term_tsc = cur_tsc + TERM_TIMEOUT; + if (lconf->msg.req && lconf->msg.type == LCONF_MSG_STOP) { + lconf->flags &= ~LCONF_FLAG_RUNNING; + break; + } + if (!lconf_is_req(lconf)) { + lconf_unset_req(lconf); + plog_warn("Command ignored (lconf functions not supported in Packet Framework pipelines)\n"); + } + } + + for (uint8_t task_id = 0; task_id < nb_tasks; ++task_id) { + rte_pipeline_flush(pipes[task_id]->p); + } + } + + for (uint8_t task_id = 0; task_id < nb_tasks; ++task_id) { + rte_pipeline_run(pipes[task_id]->p); + } + } + return 0; +} diff --git a/VNFs/DPPD-PROX/thread_pipeline.h b/VNFs/DPPD-PROX/thread_pipeline.h new file mode 100644 index 00000000..35cb64c7 --- /dev/null +++ b/VNFs/DPPD-PROX/thread_pipeline.h @@ -0,0 +1,60 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _THREAD_PIPELINE_H_ +#define _THREAD_PIPELINE_H_ + +#include <rte_pipeline.h> + +#include "lconf.h" +#include "task_base.h" + +/* Tasks based on Packet Framework pipelines */ +struct task_pipe { + struct task_base base; + + struct rte_pipeline *p; + uint32_t port_in_id[MAX_RINGS_PER_TASK]; + uint32_t port_out_id[MAX_RINGS_PER_TASK]; + uint32_t table_id[MAX_RINGS_PER_TASK]; + uint8_t n_ports_in; + uint8_t n_ports_out; + uint8_t n_tables; +}; + +/* Helper function: create pipeline, input ports and output ports */ +void init_pipe_create_in_out(struct task_pipe *tpipe, struct task_args *targ); + +/* Helper function: connect pipeline input ports to one pipeline table */ +void init_pipe_connect_one(struct task_pipe *tpipe, struct task_args *targ, uint32_t table_id); + +/* Helper function: connect pipeline input ports to all pipeline tables */ +void init_pipe_connect_all(struct task_pipe *tpipe, struct task_args *targ); + +/* Helper function: enable pipeline input ports */ +void init_pipe_enable(struct task_pipe *tpipe, struct task_args *targ); + +/* Helper function: check pipeline consistency */ +void init_pipe_check(struct task_pipe *tpipe, struct task_args *targ); + +/* This function will panic on purpose: tasks based on Packet Framework + pipelines should not be invoked via the usual task_base.handle_bulk method */ +int handle_pipe(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + +/* The pipeline thread can only run tasks based on Packet Framework pipelines */ +int thread_pipeline(struct lcore_cfg *lconf); + +#endif /* _THREAD_PIPELINE_H_ */ diff --git a/VNFs/DPPD-PROX/toeplitz.c b/VNFs/DPPD-PROX/toeplitz.c new file mode 100644 index 00000000..62424579 --- /dev/null +++ b/VNFs/DPPD-PROX/toeplitz.c @@ -0,0 +1,60 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <stdio.h> +#include <stdint.h> +#include "toeplitz.h" + +/* From XL710 Datasheet, 7.1.10 */ + +uint8_t toeplitz_init_key[TOEPLITZ_KEY_LEN] = + {0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x8f, 0xb0, + 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, + 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, + 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, + 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +uint32_t toeplitz_hash(uint8_t *buf_p, int buflen) +{ + uint32_t result = 0; + uint8_t *key_p = toeplitz_init_key; + uint8_t byte, *byte4_p = key_p+4; + int i, pos = 0; + int bit = 0; + uint32_t key_word = __builtin_bswap32(*(uint32_t *)key_p); + + for (i = 0; i < buflen; ++i) { + byte = buf_p[i]; + for (bit = 0; bit <= 7; ++bit) { + if (byte & (1 << (7 - bit))) { + result ^= key_word; + } + key_word = (key_word << 1) | ((*byte4_p >> (7 - bit)) & 1); + } + if (pos >= TOEPLITZ_KEY_LEN - 4) { + pos = 0; + byte4_p = key_p; + } + else { + pos++; + byte4_p++; + } + } + return result; +} diff --git a/VNFs/DPPD-PROX/toeplitz.h b/VNFs/DPPD-PROX/toeplitz.h new file mode 100644 index 00000000..f24ae766 --- /dev/null +++ b/VNFs/DPPD-PROX/toeplitz.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TOEPLITZ_H_ +#define _TOEPLITZ_H_ + +#define TOEPLITZ_KEY_LEN 52 +extern uint8_t toeplitz_init_key[TOEPLITZ_KEY_LEN]; +uint32_t toeplitz_hash(uint8_t *buf_p, int buflen); +#endif diff --git a/VNFs/DPPD-PROX/token_time.h b/VNFs/DPPD-PROX/token_time.h new file mode 100644 index 00000000..e59647ad --- /dev/null +++ b/VNFs/DPPD-PROX/token_time.h @@ -0,0 +1,165 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TOKEN_TIME_H_ +#define _TOKEN_TIME_H_ + +#include <rte_cycles.h> +#include <math.h> + +#include "prox_assert.h" + +struct token_time_cfg { + uint64_t bpp; + uint64_t period; + uint64_t bytes_max; +}; + +struct token_time { + uint64_t tsc_last; + uint64_t tsc_last_bytes; + uint64_t bytes_now; + struct token_time_cfg cfg; +}; + +/* Convert a given fractional bytes per period into bpp with as + minimal loss of accuracy. */ +static struct token_time_cfg token_time_cfg_create(double frac, uint64_t period, uint64_t bytes_max) +{ + struct token_time_cfg ret; + + /* Since period is expressed in units of cycles and it is in + most cases set to 1 second (which means its value is <= + 3*10^9) and 2^64/10^9 > 6148914691 > 2^32). This means that + at most, period and frac will be doubled 32 times by the + following algorithm. Hence, the total error introduced by + the chosen values for bpp and period will be between 0 and + 1/2^33. Note that since there are more operations that + can't overflow, the actual accuracy will probably be + lower. */ + + /* The reason to limit period by UINT64_MAX/(uint64_t)frac is + that at run-time, the token_time_update function will + multiply a number that is <= period with bpp. In addition, + the token_time_tsc_until function will multiply at most + bytes_max with period so make sure that can't overflow. */ + + while (period < UINT64_MAX/2 && frac != floor(frac) && + (frac < 2.0f || period < UINT64_MAX/4/(uint64_t)frac) && + (bytes_max == UINT64_MAX || period < UINT64_MAX/2/bytes_max)) { + period *= 2; + frac *= 2; + } + + ret.bpp = floor(frac + 0.5); + ret.period = period; + ret.bytes_max = bytes_max; + + return ret; +} + +static void token_time_update(struct token_time *tt, uint64_t tsc) +{ + uint64_t new_bytes; + uint64_t t_diff = tsc - tt->tsc_last; + + /* Since the rate is expressed in tt->bpp, i.e. bytes per + period, counters can only be incremented/decremented + accurately every period cycles. */ + + /* If the last update was more than a period ago, the update + can be performed accurately. */ + if (t_diff > tt->cfg.period) { + /* First add remaining tokens in the last period that + was added partially. */ + new_bytes = tt->cfg.bpp - tt->tsc_last_bytes; + tt->tsc_last_bytes = 0; + tt->bytes_now += new_bytes; + t_diff -= tt->cfg.period; + tt->tsc_last += tt->cfg.period; + + /* If now it turns out that more periods have elapsed, + add the bytes for those periods directly. */ + if (t_diff > tt->cfg.period) { + uint64_t periods = t_diff/tt->cfg.period; + + tt->bytes_now += periods * tt->cfg.bpp; + t_diff -= tt->cfg.period * periods; + tt->tsc_last += tt->cfg.period * periods; + } + } + + /* At this point, t_diff will be guaranteed to be less + than tt->cfg.period. */ + new_bytes = t_diff * tt->cfg.bpp/tt->cfg.period - tt->tsc_last_bytes; + tt->tsc_last_bytes += new_bytes; + tt->bytes_now += new_bytes; + if (tt->bytes_now > tt->cfg.bytes_max) + tt->bytes_now = tt->cfg.bytes_max; +} + +static void token_time_set_bpp(struct token_time *tt, uint64_t bpp) +{ + tt->cfg.bpp = bpp; +} + +static void token_time_init(struct token_time *tt, const struct token_time_cfg *cfg) +{ + tt->cfg = *cfg; +} + +static void token_time_reset(struct token_time *tt, uint64_t tsc, uint64_t bytes_now) +{ + tt->tsc_last = tsc; + tt->bytes_now = bytes_now; + tt->tsc_last_bytes = 0; +} + +static void token_time_reset_full(struct token_time *tt, uint64_t tsc) +{ + token_time_reset(tt, tsc, tt->cfg.bytes_max); +} + +static int token_time_take(struct token_time *tt, uint64_t bytes) +{ + if (bytes > tt->bytes_now) + return -1; + tt->bytes_now -= bytes; + return 0; +} + +static void token_time_take_clamp(struct token_time *tt, uint64_t bytes) +{ + if (bytes > tt->bytes_now) + tt->bytes_now = 0; + else + tt->bytes_now -= bytes; +} + +static uint64_t token_time_tsc_until(const struct token_time *tt, uint64_t bytes) +{ + if (tt->bytes_now >= bytes) + return 0; + + return (bytes - tt->bytes_now) * tt->cfg.period / tt->cfg.bpp; +} + +static uint64_t token_time_tsc_until_full(const struct token_time *tt) +{ + return token_time_tsc_until(tt, tt->cfg.bytes_max); +} + +#endif /* _TOKEN_TIME_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/Makefile b/VNFs/DPPD-PROX/tools/flow_extract/Makefile new file mode 100644 index 00000000..a772b8c5 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/Makefile @@ -0,0 +1,59 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +SOURCES = main.cpp +SOURCES += streamextract.cpp +SOURCES += pcapreader.cpp +SOURCES += pcapwriter.cpp +SOURCES += timestamp.cpp +SOURCES += pcappkt.cpp +SOURCES += netsocket.cpp +SOURCES += stream3.cpp +SOURCES += stream2.cpp +SOURCES += stream.cpp +SOURCES += path.cpp +SOURCES += allocator.cpp +SOURCES += halfstream.cpp +SOURCES += bundle.cpp +SOURCES += progress.cpp +SOURCES += mappedfile.cpp +SOURCES += streamsorter.cpp +SOURCES += memreader.cpp +SOURCES += programconfig.cpp + +BUILD_DIR = build +OBJECTS = $(SOURCES:%.cpp=$(BUILD_DIR)/%.o) +PROG = flowextract + +CXXFLAGS += -D__STDC_LIMIT_MACROS -g -O2 -Wall -ansi -pedantic -Wno-unused -msse4.2 +LDFLAGS = -lpcap + +$(BUILD_DIR)/$(PROG): $(OBJECTS) + @echo -e "LD\t$<" + @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + +-include $(SOURCES:%.cpp=$(BUILD_DIR)/%.d) + +$(BUILD_DIR)/%.o: %.cpp + @mkdir -p $(BUILD_DIR) + @echo -e "CXX\t $<" + @$(CXX) -c $(CXXFLAGS) $*.cpp -o $@ + @$(CXX) -MM $(CXXFLAGS) $*.cpp -MT $(BUILD_DIR)/$*.o > $(BUILD_DIR)/$*.d + @cp -f $(BUILD_DIR)/$*.d $(BUILD_DIR)/$*.d.tmp + @sed -e 's/.*://' -e 's/\\$$//' < $(BUILD_DIR)/$*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILD_DIR)/$*.d + @rm -f $(BUILD_DIR)/$*.d.tmp +clean: + @rm -f $(BUILD_DIR)/$(PROG) $(BUILD_DIR)/*.o $(BUILD_DIR)/*.d diff --git a/VNFs/DPPD-PROX/tools/flow_extract/README b/VNFs/DPPD-PROX/tools/flow_extract/README new file mode 100644 index 00000000..fb8754b3 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/README @@ -0,0 +1,20 @@ +## +## Copyright (c) 2010-2017 Intel Corporation +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +The flow extract tool is meant a to be run as a first pass on a pcap +file. The output is a lua config file describing the relations between +flows together with a binary file that contains all the packet headers +and payload. diff --git a/VNFs/DPPD-PROX/tools/flow_extract/allocator.cpp b/VNFs/DPPD-PROX/tools/flow_extract/allocator.cpp new file mode 100644 index 00000000..c861ebfe --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/allocator.cpp @@ -0,0 +1,84 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <cerrno> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#define USEHP + +using namespace std; + +#include "allocator.hpp" + +Allocator::Allocator(size_t size, size_t threshold) + : m_size(size), m_threshold(threshold), m_alloc_offset(0) +{ +#ifdef USEHP + int fd = open("/mnt/huge/hp", O_CREAT | O_RDWR, 0755); + if (fd < 0) { + cerr << "Allocator failed to open huge page file descriptor: " << strerror(errno) << endl; + exit(EXIT_FAILURE); + } + m_mem = (uint8_t *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m_mem == MAP_FAILED) { + perror("mmap"); + unlink("/mnt/huge"); + cerr << "Allocator mmap failed: " << strerror(errno) << endl; + exit (EXIT_FAILURE); + } +#else + m_mem = new uint8_t[size]; +#endif +} + +Allocator::~Allocator() +{ +#ifdef USEHP + munmap((void *)m_mem, m_size); +#else + delete[] m_mem; +#endif +} + +void *Allocator::alloc(size_t size) +{ + void *ret = &m_mem[m_alloc_offset]; + + m_alloc_offset += size; + return ret; +} + +void Allocator::reset() +{ + m_alloc_offset = 0; +} + +size_t Allocator::getFreeSize() const +{ + return m_size - m_alloc_offset; +} + +bool Allocator::lowThresholdReached() const +{ + return (m_size - m_alloc_offset) < m_threshold; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/allocator.hpp b/VNFs/DPPD-PROX/tools/flow_extract/allocator.hpp new file mode 100644 index 00000000..d3f1537e --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/allocator.hpp @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _ALLOCATOR_H_ +#define _ALLOCATOR_H_ + +#include <cstddef> +#include <inttypes.h> + +class Allocator { +public: + Allocator(size_t size, size_t lowThreshold); + ~Allocator(); + bool lowThresholdReached() const; + void *alloc(size_t size); + void reset(); + size_t getFreeSize() const; +private: + size_t m_size; + size_t m_threshold; + size_t m_alloc_offset; + uint8_t *m_mem; +}; + +#endif /* _ALLOCATOR_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/bundle.cpp b/VNFs/DPPD-PROX/tools/flow_extract/bundle.cpp new file mode 100644 index 00000000..abeaf14e --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/bundle.cpp @@ -0,0 +1,28 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "bundle.hpp" + +void Bundle::toLua(ofstream *f, const string& streamTableName, uint32_t idx) const +{ + (*f) << "bundles[" << idx << "] = {"; + + for(vector<uint32_t>::const_iterator i = streams.begin(); i != streams.end(); ++i) { + (*f) << streamTableName << "[" << (*i) << "]," ; + } + + (*f) << "}" << endl; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/bundle.hpp b/VNFs/DPPD-PROX/tools/flow_extract/bundle.hpp new file mode 100644 index 00000000..cb5d81b6 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/bundle.hpp @@ -0,0 +1,38 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _BUNDLE_H_ +#define _BUNDLE_H_ + +#include <vector> +#include <inttypes.h> +#include <fstream> + +using namespace std; + +class Bundle +{ +public: + void addStream(uint32_t streamId, uint32_t port) {streams.push_back(streamId); ports.push_back(port);} + const vector<uint32_t>& getStream() const {return streams;} + const vector<uint32_t>& getPorts() const {return ports;} + void toLua(ofstream *f, const string& streamTableName, uint32_t idx) const; +private: + vector<uint32_t> streams; + vector<uint32_t> ports; +}; + +#endif /* _BUNDLE_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/crc.hpp b/VNFs/DPPD-PROX/tools/flow_extract/crc.hpp new file mode 100644 index 00000000..713b4abd --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/crc.hpp @@ -0,0 +1,51 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CRC_H_ +#define _CRC_H_ + +static uint32_t crc32(const uint8_t *buf, size_t len, int init) +{ + uint32_t ret = init; + + while (len/8) { + ret = __builtin_ia32_crc32di(ret, *((uint64_t*)buf)); + len -= 8; + buf += 8; + } + + while (len/4) { + ret = __builtin_ia32_crc32si(ret, *((uint32_t*)buf)); + len -= 4; + buf += 4; + } + + while (len/2) { + ret = __builtin_ia32_crc32hi(ret, *((uint16_t*)buf)); + len -= 2; + buf += 2; + } + + while (len) { + ret = __builtin_ia32_crc32qi(ret, *((uint8_t*)buf)); + len -= 1; + buf += 1; + } + + return ret; +} + +#endif /* _CRC_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.cpp b/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.cpp new file mode 100644 index 00000000..909fc94d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.cpp @@ -0,0 +1,67 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <cstdlib> +#include <cstring> +#include <stdint.h> + +#include "csvfilereader.hpp" + +int CsvFileReader::open(const string& str) +{ + char *resolved_path = new char[1024]; + + memset(resolved_path, 0, 1024); + realpath(str.c_str(), resolved_path); + file.open(resolved_path); + + delete []resolved_path; + return file.is_open(); +} + +vector<string> CsvFileReader::read() +{ + vector<string> ret; + size_t prev = 0, cur = 0; + string line; + + if (file.eof()) + return vector<string>(); + + std::getline(file, line); + if (line.empty()) + return vector<string>(); + + while (true) { + cur = line.find_first_of(',', prev); + + if (cur != SIZE_MAX) { + ret.push_back(line.substr(prev, cur - prev)); + prev = cur + 1; + } + else { + ret.push_back(line.substr(prev, line.size() - prev)); + break; + } + } + return ret; +} + +void CsvFileReader::close() +{ + file.close(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.hpp b/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.hpp new file mode 100644 index 00000000..21f397a7 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/csvfilereader.hpp @@ -0,0 +1,35 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _CSVFILE_H_ +#define _CSVFILE_H_ + +#include <fstream> +#include <string> +#include <vector> + +using namespace std; + +class CsvFileReader { +public: + int open(const string& str); + vector<string> read(); + void close(); +private: + ifstream file; +}; + +#endif /* _CSVFILE_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/flowtable.hpp b/VNFs/DPPD-PROX/tools/flow_extract/flowtable.hpp new file mode 100644 index 00000000..ebb4d927 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/flowtable.hpp @@ -0,0 +1,174 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _FLOWTABLE_H_ +#define _FLOWTABLE_H_ + +#include <inttypes.h> +#include <sys/time.h> +#include <stdio.h> +#include <cstring> + +#include <vector> +#include <list> +#include <cstddef> +#include <utility> + +#include "crc.hpp" +#include "timestamp.hpp" + +using namespace std; + +template <typename K, typename T> +class FlowTable { +public: + struct entry { + entry(K key, T value, const struct timeval& tv, list<struct entry> *parent) : + key(key), value(value), tv(tv), parent(parent) {} + bool expired(const Timestamp &now, const Timestamp &maxDiff) const + { + return now - Timestamp(tv) > maxDiff; + } + K key; + T value; + struct timeval tv; /* List time entry has been hit */ + list<struct entry> *parent; + }; + class Iterator { + friend class FlowTable; + public: + bool operator!=(const Iterator& other) { + return m_v != other.m_v || + m_vec_pos != other.m_vec_pos || + m_a != other.m_a; + + } + Iterator& operator++() { + m_a++; + while (m_vec_pos != m_v->size() - 1 && m_a == (*m_v)[m_vec_pos].end()) { + m_vec_pos++; + m_a = (*m_v)[m_vec_pos].begin(); + } + + return *this; + } + struct entry &operator*() { + return *m_a; + } + private: + Iterator(uint32_t vec_pos, vector<list<struct entry> > *v) + : m_vec_pos(vec_pos), m_v(v) + { + m_a = (*m_v)[vec_pos].begin(); + while (m_vec_pos != m_v->size() - 1 && m_a == (*m_v)[m_vec_pos].end()) { + m_vec_pos++; + m_a = (*m_v)[m_vec_pos].begin(); + } + } + Iterator(uint32_t vec_pos, vector<list<struct entry> > *v, const typename list< struct entry>::iterator& a) + : m_vec_pos(vec_pos), m_v(v), m_a(a) + { } + uint32_t m_vec_pos; + vector<list<struct entry> > *m_v; + typename list<struct entry>::iterator m_a; + }; + uint32_t getEntryCount() const {return m_entryCount;} + FlowTable(uint32_t size); + void expire(const struct timeval& tv); + struct entry* lookup(const K& key); + void remove(struct FlowTable<K,T>::entry* entry); + struct entry* insert(const K& key, const T& value, const struct timeval& tv); + Iterator begin() {return Iterator(0, &m_elems);} + Iterator end() {return Iterator(m_elems.size() - 1, &m_elems, m_elems.back().end());} + void clear(); +private: + void clearBucket(list<struct entry> *l); + vector<list<struct entry> > m_elems; + uint32_t m_entryCount; +}; + +template <typename K, typename T> +FlowTable<K, T>::FlowTable(uint32_t size) + : m_elems(), m_entryCount(0) + +{ + m_elems.resize(size); +} + +template <typename K, typename T> +struct FlowTable<K, T>::entry* FlowTable<K, T>::lookup(const K& key) +{ + uint32_t ret = crc32((uint8_t*)&key, sizeof(K), 0); + + list<struct entry> &l = m_elems[ret % m_elems.size()]; + + if (l.empty()) + return NULL; + + for (typename list<struct entry>::iterator it = l.begin(); it != l.end(); ++it) { + if (memcmp(&((*it).key), &key, sizeof(key)) == 0) + return &(*it); + } + return NULL; +} + +template <typename K, typename T> +struct FlowTable<K, T>::entry *FlowTable<K, T>::insert(const K& key, const T& value, const struct timeval& tv) +{ + uint32_t ret = crc32((uint8_t*)&key, sizeof(K), 0); + list<struct entry> &l = m_elems[ret % m_elems.size()]; + + l.push_back(entry(key, value, tv, &l)); + + struct entry &n = l.back(); + m_entryCount++; + n.key = key; + n.value = value; + return &n; +} + +template <typename K, typename T> +void FlowTable<K, T>::remove(struct FlowTable<K,T>::entry* entry) +{ + list<struct entry> &l = *entry->parent; + + for (typename list<struct entry>::iterator it = l.begin(); it != l.end(); ++it) { + if (memcmp(&((*it).key), &entry->key, sizeof(entry->key)) == 0) { + l.erase(it); + m_entryCount--; + return ; + } + } +} + +template <typename K, typename T> +void FlowTable<K, T>::clearBucket(list<struct entry> *l) +{ + while (!l->empty()) { + m_entryCount--; + l->erase(l->begin()); + } +} + +template <typename K, typename T> +void FlowTable<K, T>::clear() +{ + for (size_t i = 0; i < m_elems.size(); ++i) { + clearBucket(&m_elems[i]); + } +} + +#endif /* _FLOWTABLE_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/halfstream.cpp b/VNFs/DPPD-PROX/tools/flow_extract/halfstream.cpp new file mode 100644 index 00000000..7d8f1fe2 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/halfstream.cpp @@ -0,0 +1,101 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <fstream> +#include <arpa/inet.h> + +#include "halfstream.hpp" + +HalfStream::Action::Part HalfStream::addPkt(const PcapPkt &pkt) +{ + const uint32_t pktId = pkts.size(); + const uint8_t *l5; + uint32_t l5Len; + uint16_t tmpHdrLen; + + const struct PcapPkt::tcp_hdr *tcp; + + struct pkt_tuple pt = pkt.parsePkt((const uint8_t **)&tcp, &tmpHdrLen, &l5, &l5Len); + + if (pt.proto_id == IPPROTO_TCP) { + if (tcp->tcp_flags & 0x02) + tcpOpen = true; + if (tcp->tcp_flags & 0x01) + tcpClose = true; + } + + if (pkts.empty()) { + first = pkt.ts(); + hdrLen = tmpHdrLen; + memcpy(hdr, pkt.payload(), hdrLen); + } + last = pkt.ts(); + totLen += pkt.len(); + contentLen += l5Len; + + pkts.push_back(pkt); + + return Action::Part(pktId, l5 - pkt.payload(), l5Len); +} + +double HalfStream::getRate() const +{ + if (pkts.empty()) + return 0; + if (first == last) + return 1250000000; + + return totLen / (last - first); +} + +HalfStream::Action::Action(HalfStream* stream, const Part &p, bool isClient) + : halfStream(stream), m_isClient(isClient) +{ + addPart(p); +} + +void HalfStream::Action::addPart(const Part &p) +{ + parts.push_back(p); +} + +uint32_t HalfStream::Action::totLen() const +{ + uint32_t ret = 0; + + for (list<Part>::const_iterator i = parts.begin(); i != parts.end(); ++i) { + ret += (*i).len; + } + + return ret; +} + +void HalfStream::Action::toFile(ofstream *f) const +{ + for (list<Part>::const_iterator i = parts.begin(); i != parts.end(); ++i) { + const PcapPkt &pkt = halfStream->pkts[i->pktId]; + const uint8_t *payload = &pkt.payload()[i->offset]; + const uint16_t len = i->len; + + f->write((const char *)payload, len); + } +} + +HalfStream::HalfStream() + : first(0, 0), last(0, 0), totLen(0), hdrLen(0), contentLen(0), tcpOpen(false), tcpClose(false) +{ + +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/halfstream.hpp b/VNFs/DPPD-PROX/tools/flow_extract/halfstream.hpp new file mode 100644 index 00000000..6216979d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/halfstream.hpp @@ -0,0 +1,63 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <list> +#include <vector> + +#include "timestamp.hpp" +#include "pcappkt.hpp" + +struct HalfStream { + struct Action { + public: + struct Part { + Part(uint32_t pktId, uint32_t offset, uint32_t len) + : pktId(pktId), offset(offset), len(len) {} + uint32_t pktId; + uint32_t offset; + uint32_t len; + }; + + Action(HalfStream* stream, const Part &p, bool isClient); + void addPart(const Part& p); + bool isClient() const {return m_isClient;} + /* An action can consist of multiple + packets. The data is not stored in the + action. Instead, a packet id together with + an offset into the packet and a length is + kept to save space */ + void toFile(ofstream* f) const; + uint32_t totLen() const; + private: + HalfStream *halfStream; + bool m_isClient; + list<Part> parts; + }; + + HalfStream(); + Timestamp first; + Timestamp last; + uint64_t totLen; + uint64_t hdrLen; + uint8_t hdr[64]; + vector<PcapPkt> pkts; + uint64_t contentLen; + bool tcpOpen; + bool tcpClose; + Action::Part addPkt(const PcapPkt &pkt); + double getRate() const; +}; diff --git a/VNFs/DPPD-PROX/tools/flow_extract/main.cpp b/VNFs/DPPD-PROX/tools/flow_extract/main.cpp new file mode 100644 index 00000000..d1476c5f --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/main.cpp @@ -0,0 +1,37 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <cstdlib> + +#include "streamextract.hpp" + +using namespace std; + +int main(int argc, char *argv[]) +{ + ProgramConfig programConfig; + + if (programConfig.parseOptions(argc, argv)) { + cerr << programConfig.getError() << endl; + cerr << programConfig.getUsage() << endl; + return EXIT_FAILURE; + } + + StreamExtract se(programConfig); + + return se.run(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.cpp b/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.cpp new file mode 100644 index 00000000..b2d1a9da --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.cpp @@ -0,0 +1,109 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <cstdlib> +#include <cstdio> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <iostream> +#include <cerrno> +#include <sys/mman.h> +#include <cstring> + +#include "mappedfile.hpp" + +static void zeroOutFile(int fd, size_t size) +{ + void *empty = calloc(1, 4096); + + while (size > 4096) { + write(fd, empty, 4096); + size -= 4096; + } + write(fd, empty, size); + free(empty); +} + +int MappedFile::open(const string& filePath, size_t size) +{ + mappedFileSize = size; + + fd = ::open(filePath.c_str(), O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + cerr << "Failed to open file " << filePath << ":" << strerror(errno) << endl; + return -1; + } + + zeroOutFile(fd, size); + data = mmap(NULL, mappedFileSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) { + cerr << "Failed to map file: " << strerror(errno) << endl; + return -1; + } + return 0; +} + +static size_t getFileSize(const string& filePath) +{ + struct stat s; + if (stat(filePath.c_str(), &s)) + return -1; + + return s.st_size; +} + +int MappedFile::open(const string& filePath) +{ + mappedFileSize = getFileSize(filePath); + + fd = ::open(filePath.c_str(), O_RDONLY); + if (fd < 0) { + cerr << "Failed to open file " << filePath << ":" << strerror(errno) << endl; + return -1; + } + + data = mmap(NULL, mappedFileSize, PROT_READ, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) { + cerr << "Failed to map file: " << strerror(errno) << endl; + return -1; + } + return 0; +} + +int MappedFile::sync() +{ + if (msync(data, mappedFileSize, MS_SYNC) == -1) { + cerr << "Failed to sync: " << strerror(errno) << endl; + return -1; + } + return 0; +} + + +void MappedFile::close() +{ + sync(); + munmap(data, mappedFileSize); + ::close(fd); +} + +size_t MappedFile::size() const +{ + return mappedFileSize; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.hpp b/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.hpp new file mode 100644 index 00000000..7bf79df5 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/mappedfile.hpp @@ -0,0 +1,40 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _MAPPEDFILE_H_ +#define _MAPPEDFILE_H_ + +#include <inttypes.h> +#include <string> + +using namespace std; + +class MappedFile { +public: + int open(const string& filePath, size_t size); + int open(const string& filePath); + void close(); + int sync(); + uint8_t* getMapBeg() {return (uint8_t *)data;} + uint8_t* getMapEnd() {return (uint8_t *)data + mappedFileSize;} + size_t size() const; +private: + int fd; + size_t mappedFileSize; + void *data; +}; + +#endif /* _MAPPEDFILE_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/memreader.cpp b/VNFs/DPPD-PROX/tools/flow_extract/memreader.cpp new file mode 100644 index 00000000..df77631c --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/memreader.cpp @@ -0,0 +1,106 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <cstdlib> + +#include "memreader.hpp" +#include "mappedfile.hpp" +#include "stream3.hpp" + +MemReader::MemReader(MappedFile *file, const vector<size_t> &offsets) +{ + initRanges(file->getMapBeg(), file->getMapEnd(), offsets); +} + +bool MemReader::read(Stream3 *stream) +{ + if (ranges.empty()) + return false; + + readStream(stream, getLowestID()); + removeEmptyRanges(); + return true; +} + +uint32_t MemReader::getLowestID() const +{ + uint32_t lowestID = UINT32_MAX; + uint32_t rangeID; + + for (size_t i = 0; i < ranges.size(); ++i) { + rangeID = Stream3::getIDFromMem(ranges[i].first); + if (rangeID < lowestID) + lowestID = rangeID; + } + return lowestID; +} + +void MemReader::readStream(Stream3 *stream, uint32_t id) +{ + stream->removeAllPackets(); + stream->setID(id); + + size_t len = 0; + for (size_t i = 0; i < ranges.size(); ++i) { + if (Stream3::getIDFromMem(ranges[i].first) == id) { + stream->addFromMemory(ranges[i].first, &len); + ranges[i].first += len; + } + } +} + +void MemReader::removeEmptyRanges() +{ + vector<pair <uint8_t *, uint8_t *> > original = ranges; + size_t destinationIdx = 0; + + for (size_t i = 0; i < original.size(); ++i) { + if (original[i].first < original[i].second) + ranges[destinationIdx++] = original[i]; + } + ranges.resize(destinationIdx); +} + +void MemReader::initRanges(uint8_t *begin, uint8_t *end, const vector<size_t> &offsets) +{ + ranges.resize(offsets.size()); + + totalLength = 0; + for (size_t i = 0; i < offsets.size(); ++i) { + ranges[i].first = begin + offsets[i]; + if (i != offsets.size() - 1) + ranges[i].second = begin + offsets[i + 1]; + else + ranges[i].second = end; + totalLength += ranges[i].second - ranges[i].first; + } + removeEmptyRanges(); +} + +size_t MemReader::getRangeLengths() const +{ + size_t total = 0; + + for (size_t i = 0; i < ranges.size(); ++i) { + total += ranges[i].second - ranges[i].first; + } + return total; +} + +size_t MemReader::consumed() const +{ + return totalLength - getRangeLengths(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/memreader.hpp b/VNFs/DPPD-PROX/tools/flow_extract/memreader.hpp new file mode 100644 index 00000000..31be4c31 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/memreader.hpp @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _MEMREADER_H_ +#define _MEMREADER_H_ + +#include <vector> +#include <inttypes.h> + +using namespace std; + +class Stream3; +class MappedFile; + +class MemReader { +public: + MemReader(MappedFile *file, const vector<size_t> &offsets); + bool read(Stream3 *stream); + size_t getTotalLength() const {return totalLength;} + size_t consumed() const; +private: + size_t getRangeLengths() const; + uint32_t getLowestID() const; + void removeEmptyRanges(); + void readStream(Stream3 *stream, uint32_t id); + void initRanges(uint8_t *begin, uint8_t *end, const vector<size_t> &offsets); + + size_t totalLength; + vector<pair <uint8_t *, uint8_t *> > ranges; +}; + +#endif /* _MEMREADER_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/netsocket.cpp b/VNFs/DPPD-PROX/tools/flow_extract/netsocket.cpp new file mode 100644 index 00000000..8c61ba7d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/netsocket.cpp @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "netsocket.hpp" + +NetSocket::NetSocket(uint32_t host, uint16_t port) + : host(host), port(port) +{ + +} + +bool NetSocket::operator>(const NetSocket& other) const +{ + return host > other.host || (host == other.host && port > other.port); +} + +bool NetSocket::operator<(const NetSocket& other) const +{ + return host < other.host || (host == other.host && port < other.port); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/netsocket.hpp b/VNFs/DPPD-PROX/tools/flow_extract/netsocket.hpp new file mode 100644 index 00000000..bfd6bec9 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/netsocket.hpp @@ -0,0 +1,31 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _NETSOCKET_H_ +#define _NETSOCKET_H_ + +#include <inttypes.h> + +struct NetSocket { + NetSocket() {} + NetSocket(uint32_t host, uint16_t port); + bool operator>(const NetSocket& other) const; + bool operator<(const NetSocket& other) const; + uint32_t host; + uint16_t port; +}; + +#endif /* _NETSOCKET_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/path.cpp b/VNFs/DPPD-PROX/tools/flow_extract/path.cpp new file mode 100644 index 00000000..7d94aae6 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/path.cpp @@ -0,0 +1,97 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <iomanip> +#include <sys/stat.h> +#include <sstream> +#include <fstream> + +#include "path.hpp" + +bool Path::isDir() const +{ + struct stat s = { 0 }; + + if (stat(path.c_str(), &s)) { + return false; + } + + return s.st_mode & S_IFDIR; +} + +bool Path::isFile() const +{ + struct stat s = { 0 }; + + if (stat(path.c_str(), &s)) { + return false; + } + + return s.st_mode & S_IFREG; +} + +Path Path::add(const string& str) const +{ + stringstream ss; + + ss << path << str; + + return Path(ss.str()); +} + +Path Path::add(int number) const +{ + stringstream ss; + + ss << path << number; + + return Path(ss.str()); +} + +Path &Path::concat(const string &add) +{ + stringstream ss; + + ss << path << add; + path = ss.str(); + + return *this; +} + +int Path::mkdir() const +{ + if (!isDir()) + return ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + return 0; +} + +std::ostream& operator<<(std::ofstream &stream, const Path &p) +{ + stream << p.path.c_str(); + + return stream; +} + +string Path::getFileName() const +{ + for (size_t i = path.size() - 1; i >= 0; --i) { + if (path[i] == '/') { + return path.substr(i + 1); + } + } + return path; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/path.hpp b/VNFs/DPPD-PROX/tools/flow_extract/path.hpp new file mode 100644 index 00000000..e56c9050 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/path.hpp @@ -0,0 +1,42 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PATH_H_ +#define _PATH_H_ + +#include <string> + +using namespace std; + +class Path { +public: + Path(); + Path(const Path& other) : path(other.path) {} + Path(const string& str) : path(str) {} + Path add(const string& str) const; + Path add(int number) const; + Path &concat(const string &str); + const string& str() const {return path;} + bool isDir() const; + bool isFile() const; + string getFileName() const; + int mkdir() const; + friend std::ostream& operator<<(std::ofstream &stream, const Path &path); +private: + string path; +}; + +#endif /* _PATH_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.cpp b/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.cpp new file mode 100644 index 00000000..91708bb1 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.cpp @@ -0,0 +1,266 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <pcap.h> +#include <inttypes.h> +#include <cstring> +#include <arpa/inet.h> +#include <iostream> +#include <fstream> +#include <cstdlib> +#include "allocator.hpp" +#include "pcappkt.hpp" + +Allocator *PcapPkt::allocator = NULL; + +void* PcapPkt::operator new(size_t size) +{ + if (allocator) + return allocator->alloc(size); + else + return ::operator new(size); +} + +void PcapPkt::operator delete(void *pointer) +{ + if (!allocator) + :: operator delete(pointer); +} + +PcapPkt::PcapPkt(uint8_t *mem) +{ + header = *(struct pcap_pkthdr *)mem; + mem += sizeof(header); + buf = new uint8_t[header.len]; + memcpy(buf, mem, header.len); +} + +PcapPkt::PcapPkt() +{ + buf = new uint8_t[1514]; + memset(&header, 0, sizeof(header)); +} + +PcapPkt::PcapPkt(const PcapPkt& other) +{ + if (!allocator) { + buf = new uint8_t[other.len()]; + } + else { + buf = (uint8_t *)allocator->alloc(other.len()); + } + + memcpy(buf, other.buf, other.len()); + header = other.header; +} + +PcapPkt::~PcapPkt() +{ + if (!allocator) + delete[] buf; +} + +#define ETYPE_IPv4 0x0008 /* IPv4 in little endian */ +#define ETYPE_IPv6 0xDD86 /* IPv6 in little endian */ +#define ETYPE_ARP 0x0608 /* ARP in little endian */ +#define ETYPE_VLAN 0x0081 /* 802-1aq - VLAN */ +#define ETYPE_MPLSU 0x4788 /* MPLS unicast */ +#define ETYPE_MPLSM 0x4888 /* MPLS multicast */ +#define ETYPE_8021ad 0xA888 /* Q-in-Q */ +#define ETYPE_LLDP 0xCC88 /* Link Layer Discovery Protocol (LLDP) */ +#define ETYPE_EoGRE 0x5865 /* EoGRE in little endian */ + +struct ipv4_hdr { + uint8_t version_ihl; /**< version and header length */ + uint8_t type_of_service; /**< type of service */ + uint16_t total_length; /**< length of packet */ + uint16_t packet_id; /**< packet ID */ + uint16_t fragment_offset; /**< fragmentation offset */ + uint8_t time_to_live; /**< time to live */ + uint8_t next_proto_id; /**< protocol ID */ + uint16_t hdr_checksum; /**< header checksum */ + uint32_t src_addr; /**< source address */ + uint32_t dst_addr; /**< destination address */ +} __attribute__((__packed__)); + +struct ether_addr { + uint8_t addr_bytes[6]; /**< Address bytes in transmission order */ +} __attribute__((__packed__)); + +struct ether_hdr { + struct ether_addr d_addr; /**< Destination address. */ + struct ether_addr s_addr; /**< Source address. */ + uint16_t ether_type; /**< Frame type. */ +} __attribute__((__packed__)); + +struct vlan_hdr { + uint16_t vlan_tci; /**< Priority (3) + CFI (1) + Identifier Code (12) */ + uint16_t eth_proto;/**< Ethernet type of encapsulated frame. */ +} __attribute__((__packed__)); + +struct udp_hdr { + uint16_t src_port; /**< UDP source port. */ + uint16_t dst_port; /**< UDP destination port. */ + uint16_t dgram_len; /**< UDP datagram length */ + uint16_t dgram_cksum; /**< UDP datagram checksum */ +} __attribute__((__packed__)); + +struct pkt_tuple PcapPkt::parsePkt(const uint8_t **l4_hdr, uint16_t *hdr_len, const uint8_t **l5, uint32_t *l5_len) const +{ + struct pkt_tuple pt = {0}; + + const struct ether_hdr *peth = (struct ether_hdr *)buf; + int l2_types_count = 0; + const struct ipv4_hdr* pip = 0; + + switch (peth->ether_type) { + case ETYPE_IPv4: + pip = (const struct ipv4_hdr *)(peth + 1); + break; + case ETYPE_VLAN: { + const struct vlan_hdr *vlan = (const struct vlan_hdr *)(peth + 1); + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (const struct ipv4_hdr *)(peth + 1); + } + else if (vlan->eth_proto == ETYPE_VLAN) { + const struct vlan_hdr *vlan = (const struct vlan_hdr *)(peth + 1); + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (const struct ipv4_hdr *)(peth + 1); + } + else if (vlan->eth_proto == ETYPE_IPv6) { + throw 0; + } + else { + /* TODO: handle BAD PACKET */ + throw 0; + } + } + } + break; + case ETYPE_8021ad: { + const struct vlan_hdr *vlan = (const struct vlan_hdr *)(peth + 1); + if (vlan->eth_proto == ETYPE_VLAN) { + const struct vlan_hdr *vlan = (const struct vlan_hdr *)(peth + 1); + if (vlan->eth_proto == ETYPE_IPv4) { + pip = (const struct ipv4_hdr *)(peth + 1); + } + else { + throw 0; + } + } + else { + throw 0; + } + } + break; + case ETYPE_MPLSU: + break; + default: + break; + } + + /* L3 */ + if ((pip->version_ihl >> 4) == 4) { + + if ((pip->version_ihl & 0x0f) != 0x05) { + /* TODO: optional fields */ + throw 0; + } + + pt.proto_id = pip->next_proto_id; + pt.src_addr = pip->src_addr; + pt.dst_addr = pip->dst_addr; + } + else { + /* TODO: IPv6 and bad packets */ + throw 0; + } + + /* L4 parser */ + if (pt.proto_id == IPPROTO_UDP) { + const struct udp_hdr *udp = (const struct udp_hdr*)(pip + 1); + if (l4_hdr) + *l4_hdr = (const uint8_t*)udp; + if (hdr_len) + *hdr_len = (const uint8_t*)udp - buf; + pt.src_port = udp->src_port; + pt.dst_port = udp->dst_port; + if (l5) + *l5 = ((const uint8_t*)udp) + sizeof(struct udp_hdr); + if (l5_len) + *l5_len = ntohs(udp->dgram_len) - sizeof(struct udp_hdr); + } + else if (pt.proto_id == IPPROTO_TCP) { + const struct tcp_hdr *tcp = (const struct tcp_hdr *)(pip + 1); + if (l4_hdr) + *l4_hdr = (const uint8_t*)tcp; + if (hdr_len) + *hdr_len = (const uint8_t*)tcp - buf; + pt.src_port = tcp->src_port; + pt.dst_port = tcp->dst_port; + + if (l5) + *l5 = ((const uint8_t*)tcp) + ((tcp->data_off >> 4)*4); + if (l5_len) + *l5_len = ntohs(pip->total_length) - sizeof(struct ipv4_hdr) - ((tcp->data_off >> 4)*4); + } + else { + fprintf(stderr, "unsupported protocol %d\n", pt.proto_id); + throw 0; + } + + return pt; +} + +void PcapPkt::toMem(uint8_t *mem) const +{ + memcpy(mem, &header, sizeof(header)); + mem += sizeof(header); + memcpy(mem, buf, header.len); +} + +void PcapPkt::fromMem(uint8_t *mem) +{ + memcpy(&header, mem, sizeof(header)); + mem += sizeof(header); + memcpy(buf, mem, header.len); +} + +void PcapPkt::toFile(ofstream *file) const +{ + file->write(reinterpret_cast<const char *>(&header), sizeof(header)); + file->write(reinterpret_cast<const char *>(buf), header.len); +} +size_t PcapPkt::memSize() const +{ + return sizeof(header) + header.len; +} + +PcapPkt::L4Proto PcapPkt::getProto() const +{ + struct pkt_tuple pt = parsePkt(); + return pt.proto_id == IPPROTO_TCP? PROTO_TCP : PROTO_UDP; +} + +ostream& operator<<(ostream& stream, const pkt_tuple &other) +{ + stream << other.src_addr << "," + << other.dst_addr << "," + << (int)other.proto_id << "," + << other.src_port << "," + << other.dst_port; + return stream; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.hpp b/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.hpp new file mode 100644 index 00000000..e437c790 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcappkt.hpp @@ -0,0 +1,104 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PCAPPKT_H_ +#define _PCAPPKT_H_ + +#include <inttypes.h> +#include <pcap.h> +#include <string> +#include <cstring> + +using namespace std; + +struct pkt_tuple { + uint32_t src_addr; + uint32_t dst_addr; + uint8_t proto_id; + uint16_t src_port; + uint16_t dst_port; + bool operator!=(const pkt_tuple& other) const + { + return src_addr != other.src_addr || + dst_addr != other.dst_addr || + proto_id != other.proto_id || + src_port != other.src_port || + dst_port != other.dst_port; + } + bool operator==(const pkt_tuple& other) const + { + return src_addr == other.src_addr && + dst_addr == other.dst_addr && + proto_id == other.proto_id && + src_port == other.src_port && + dst_port == other.dst_port; + } + friend ostream& operator<<(ostream& stream, const pkt_tuple &other); + struct pkt_tuple flip() const + { + struct pkt_tuple ret; + + ret = *this; + ret.src_addr = dst_addr; + ret.src_port = dst_port; + ret.dst_addr = src_addr; + ret.dst_port = src_port; + return ret; + } + +} __attribute__((packed)); + +class Allocator; + +class PcapPkt { + friend class PcapReader; +public: + struct tcp_hdr { + uint16_t src_port; /**< TCP source port. */ + uint16_t dst_port; /**< TCP destination port. */ + uint32_t sent_seq; /**< TX data sequence number. */ + uint32_t recv_ack; /**< RX data acknowledgement sequence number. */ + uint8_t data_off; /**< Data offset. */ + uint8_t tcp_flags; /**< TCP flags */ + uint16_t rx_win; /**< RX flow control window. */ + uint16_t cksum; /**< TCP checksum. */ + uint16_t tcp_urp; /**< TCP urgent pointer, if any. */ + } __attribute__((__packed__)); + + static Allocator *allocator; + enum L4Proto {PROTO_TCP, PROTO_UDP}; + PcapPkt(); + void* operator new(size_t size); + static void operator delete(void *pointer); + PcapPkt(const PcapPkt& other); + PcapPkt(uint8_t *mem); + void toMem(uint8_t *mem) const; + void fromMem(uint8_t *mem); + void toFile(ofstream *file) const; + size_t memSize() const; + const struct timeval &ts() const {return header.ts;} + const uint16_t len() const {return header.len;} + pkt_tuple parsePkt(const uint8_t **l4_hdr = NULL, uint16_t *hdr_len = NULL, const uint8_t **l5 = NULL, uint32_t *l5_len = NULL) const; + const struct pcap_pkthdr &hdr() const {return header;} + const uint8_t *payload() const {return buf;} + enum L4Proto getProto() const; + ~PcapPkt(); +private: + struct pcap_pkthdr header; + uint8_t *buf; +}; + +#endif /* _PCAPPKT_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.cpp b/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.cpp new file mode 100644 index 00000000..2a0f2f05 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.cpp @@ -0,0 +1,32 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "pcappktref.hpp" + +PcapPktRef::PcapPktRef(const PcapPktRef &other) + : pos(other.pos), pr(other.pr) +{ +} + +PcapPkt PcapPktRef::getPcapPkt() +{ + PcapPkt ret; + + if (!pr->readOnce(&ret, pos)) { + cerr << "failed to read pcap from pcap pkt ref" << endl; + } + return ret; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.hpp b/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.hpp new file mode 100644 index 00000000..1afaf2a5 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcappktref.hpp @@ -0,0 +1,40 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PCAPPKTREF_H_ +#define _PCAPPKTREF_H_ + +#include <iostream> + +#include "pcapreader.hpp" +#include "pcappkt.hpp" + +using namespace std; + +class PcapPktRef +{ +public: + PcapPktRef(uint64_t pos, PcapReader *pr) : pos(pos), pr(pr) {} + PcapPktRef(const PcapPktRef &other); + PcapPktRef() : pos(0), pr(0) {} + bool isValid() const {return pos != 0;} + PcapPkt getPcapPkt(); +private: + uint64_t pos; + PcapReader *pr; +}; + +#endif /* _PCAPPKTREF_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.cpp b/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.cpp new file mode 100644 index 00000000..6b5a6734 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.cpp @@ -0,0 +1,76 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <pcap.h> +#include <cstring> +#include <linux/in.h> + +#include "pcapreader.hpp" + +int PcapReader::open(const string& file_path) +{ + char err_str[PCAP_ERRBUF_SIZE]; + + if (m_handle) { + m_error = "Pcap file already open"; + return -1; + } + + m_handle = pcap_open_offline_with_tstamp_precision(file_path.c_str(), + PCAP_TSTAMP_PRECISION_NANO, + err_str); + + if (!m_handle) { + m_error = "Failed to open pcap file"; + return -1; + } + + m_file_beg = ftell(pcap_file(m_handle)); + fseek(pcap_file(m_handle), 0, SEEK_END); + m_file_end = ftell(pcap_file(m_handle)); + fseek(pcap_file(m_handle), m_file_beg, SEEK_SET); + + return 0; +} + +int PcapReader::readOnce(PcapPkt *pkt, uint64_t pos) +{ + return -1; +} + +int PcapReader::read(PcapPkt *pkt) +{ + if (!m_handle) { + m_error = "No pcap file opened"; + } + + const uint8_t *buf = pcap_next(m_handle, &pkt->header); + + if (buf) { + memcpy(pkt->buf, buf, pkt->header.len); + pktReadCount++; + } + + return !!buf; +} + +void PcapReader::close() +{ + if (m_handle) + pcap_close(m_handle); + + m_handle = NULL; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.hpp b/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.hpp new file mode 100644 index 00000000..3766c67b --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcapreader.hpp @@ -0,0 +1,48 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PCAPREADER_H_ +#define _PCAPREADER_H_ + +#include <inttypes.h> +#include <string> + +#include <pcap.h> + +#include "pcappkt.hpp" + +using namespace std; + +class PcapReader { +public: + PcapReader() : m_handle(NULL), pktReadCount(0) {} + int open(const string& file_path); + size_t pos() {return ftell(pcap_file(m_handle)) - m_file_beg;} + size_t end() {return m_file_end;} + int read(PcapPkt *pkt); + int readOnce(PcapPkt *pkt, uint64_t pos); + size_t getPktReadCount() const {return pktReadCount;} + void close(); + const string &getError() const {return m_error;} +private: + pcap_t *m_handle; + size_t m_file_beg; + size_t m_file_end; + size_t pktReadCount; + string m_error; +}; + +#endif /* _PCAPREADER_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.cpp b/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.cpp new file mode 100644 index 00000000..4c7c4cea --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.cpp @@ -0,0 +1,46 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "pcapwriter.hpp" + +int PcapWriter::open(const string& file_path) +{ + m_handle = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, 65536, PCAP_TSTAMP_PRECISION_NANO); + if (m_handle == NULL) + return -1; + + m_pcap_dumper = pcap_dump_open(m_handle, file_path.c_str()); + if (m_pcap_dumper == NULL) { + pcap_close(m_handle); + return -1; + } + + return 0; +} + +int PcapWriter::write(const PcapPkt& pkt) +{ + pcap_dump((unsigned char *)m_pcap_dumper, &pkt.hdr(), pkt.payload()); + return 0; +} + +void PcapWriter::close() +{ + if (m_pcap_dumper) + pcap_dump_close(m_pcap_dumper); + if (m_handle) + pcap_close(m_handle); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.hpp b/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.hpp new file mode 100644 index 00000000..32f79369 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/pcapwriter.hpp @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PCAPWRITER_H_ +#define _PCAPWRITER_H_ + +#include "pcappkt.hpp" + +class PcapWriter { +public: + PcapWriter() {} + int open(const string& file_path); + int write(const PcapPkt& pkt); + void close(); +private: + pcap_t *m_handle; + pcap_dumper_t *m_pcap_dumper; +}; + +#endif /* _PCAPWRITER_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/programconfig.cpp b/VNFs/DPPD-PROX/tools/flow_extract/programconfig.cpp new file mode 100644 index 00000000..7b1e18e1 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/programconfig.cpp @@ -0,0 +1,119 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <sstream> +#include <getopt.h> +#include <iostream> +#include <cstdlib> +#include "programconfig.hpp" + +ProgramConfig::ProgramConfig() + : path_file_in_pcap(""), path_dir_out("output"), + path_file_dest_lua("lua"), max_pkts(UINT32_MAX), + max_streams(UINT32_MAX), sampleCount(20000), flowTableSize(8*1024*1024), + run_first_step(true), write_pcaps(false) +{ +} + +string ProgramConfig::getUsage() const +{ + stringstream ret; + + ret << "Usage example: "<< m_programName << " -i in.pcap\n\n" + << "Flow Extract 2.0 analyzes and extracts a traffic profile\n" + << "configuration from a pcap file. The output is a lua\n" + << "configuration file and a binary file containing all the\n" + << "headers and payloads for each stream.\n\n" + + << "The program supports analyzing large pcap file (> 300 GB).\n" + << "For this, it uses a multi-pass approach. The output of \n" + << "intermediary steps is stored in the working directory. The\n" + << "algorithm can be described by the following steps:\n\n" + << " 1. The pcap file in read chunks of 16 GB. The packets in\n" + << " each chunk are associated with streams. The streams are\n" + << " ordered through a global ID. Each stream is stored as a" + << " sequence of packets that belong to that stream. The\n" + << " resulting file at 'DIR/tmp' where DIR is specified\n" + << " through -o options as shown below.\n" + << " Each chunk in tmp is merged and the result is written\n" + << " to file1. Reading the stream with a given ID from all chunks\n" + << " gets all the packets for the stream from the whole pcap in\n" + << " memory. This first step forms is implemented by an\n" + << " external sorting algorithm.\n" + << " 2. File2 is read and the source IP for each stream is used to\n" + << " associate each stream with a bundle. SAMPLE_COUNT samples\n" + << " are taken from the set of bundles. The set of streams that\n" + << " are still referenced by the sampled bundles extracted from\n" + << " file2 and written to the final binary file. This binary file\n" + << " is referenced from the lua configuration. The lua config file\n" + << " is written out as part of this step.\n" + << "Arguments:\n" + << "-i FILE Input pcap to process\n" + << "-o DIR output directory and working directory\n" + << "-s SAMPLE_COUNT Number of samples to take (default is 20K)\n" + << "-k Skip the first step as described above. Useful to\n" + << " adjust the number of samples without having to\n" + << " repeat the whole process\n"; + + + return ret.str(); +} + +int ProgramConfig::checkConfig() +{ + if (path_file_in_pcap.empty()) { + m_error = "Missing input pcap file\n"; + return -1; + } + return 0; +} + +int ProgramConfig::parseOptions(int argc, char *argv[]) +{ + char c; + + m_programName = argv[0]; + while ((c = getopt(argc, argv, "hki:o:s:p")) != -1) { + switch (c) { + case 'h': + return -1; + break; + case 'k': + run_first_step = false; + break; + case 'i': + path_file_in_pcap = optarg; + break; + case 'o': + path_dir_out = optarg; + break; + case 's': + sampleCount = atoi(optarg); + break; + case 'p': + write_pcaps = true; + break; + case '?': + cerr << getUsage() << endl; + return 0; + default: + m_error = "Invalid parameter\n"; + return -1; + } + } + + return checkConfig(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/programconfig.hpp b/VNFs/DPPD-PROX/tools/flow_extract/programconfig.hpp new file mode 100644 index 00000000..59b7104d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/programconfig.hpp @@ -0,0 +1,47 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROGRAMCONFIG_H_ +#define _PROGRAMCONFIG_H_ + +#include <string> +#include <inttypes.h> + +using namespace std; + +class ProgramConfig { +public: + ProgramConfig(); + string getUsage() const; + int parseOptions(int argc, char *argv[]); + const string& getError() const {return m_error;} + + string path_file_in_pcap; + string path_dir_out; + string path_file_dest_lua; + uint32_t max_pkts; + uint32_t max_streams; + uint32_t sampleCount; + uint32_t flowTableSize; + bool run_first_step; + bool write_pcaps; +private: + int checkConfig(); + string m_error; + string m_programName; +}; + +#endif /* _PROGRAMCONFIG_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/progress.cpp b/VNFs/DPPD-PROX/tools/flow_extract/progress.cpp new file mode 100644 index 00000000..2c65960f --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/progress.cpp @@ -0,0 +1,96 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <sys/time.h> +#include <iostream> +#include <cstdio> +#include <sstream> + +#include "progress.hpp" + +static uint64_t getSec() +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec; +} + +Progress::Progress(size_t maxProgress, bool inPlace, bool showElapsedTime) + : maxProgress(maxProgress), curProgress(0), inPlace(inPlace), showElapsedTime(showElapsedTime), prevLength(0), title("Progress") +{ + lastRefresh = -1; + firstRefresh = getSec(); +} + +void Progress::setProgress(size_t curProgress) +{ + this->curProgress = curProgress; +} + +void Progress::setProgress() +{ + this->curProgress = maxProgress; +} + +uint32_t Progress::addDetail(const string& detail) +{ + details.push_back(make_pair(detail, 0)); + return details.size() - 1; +} + +void Progress::setDetail(uint32_t idx, uint32_t val) +{ + details[idx].second = val; +} + +bool Progress::couldRefresh() +{ + uint32_t cur = getSec(); + + return (lastRefresh != cur); +} + +void Progress::refresh(bool withNewLine) +{ + lastRefresh = getSec(); + uint64_t elapsed = lastRefresh - firstRefresh; + size_t progress = curProgress * 100 / maxProgress; + size_t remainingTime = curProgress? (elapsed * maxProgress - elapsed * curProgress) / curProgress : 0; + + stringstream ss; + + if (inPlace) + ss << "\r"; + ss << title << ": " << progress << "%"; + ss << ", remaining: " << remainingTime; + if (showElapsedTime) + ss << ", elapsed: " << elapsed; + for (size_t i = 0; i < details.size(); ++i) + ss << ", " << details[i].first << ": " << details[i].second; + + size_t prevLength2 = ss.str().size(); + + while (ss.str().size() < prevLength) + ss << " "; + prevLength = prevLength2; + + if (!inPlace || withNewLine) + ss << "\n"; + + cout << ss.str(); + cout.flush(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/progress.hpp b/VNFs/DPPD-PROX/tools/flow_extract/progress.hpp new file mode 100644 index 00000000..7f55cf98 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/progress.hpp @@ -0,0 +1,50 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _PROGRESS_H_ +#define _PROGRESS_H_ + +#include <inttypes.h> +#include <vector> +#include <utility> +#include <string> + +using namespace std; + +class Progress { +public: + Progress(size_t maxProgress, bool inPlace = true, bool showElapsedTime = true); + void setTitle(const string &prefix) {this->title = title;} + void setProgress(size_t curProgress); + void setProgress(); + uint32_t addDetail(const string& detail); + void clearDetails() {details.clear();} + void setDetail(uint32_t idx, uint32_t val); + bool couldRefresh(); + void refresh(bool withNewLine = false); +private: + uint64_t firstRefresh; + uint64_t lastRefresh; + size_t maxProgress; + size_t curProgress; + bool inPlace; + bool showElapsedTime; + size_t prevLength; + string title; + vector<pair<string, uint32_t> > details; +}; + +#endif /* _PROGRESS_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream.cpp b/VNFs/DPPD-PROX/tools/flow_extract/stream.cpp new file mode 100644 index 00000000..b8056852 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream.cpp @@ -0,0 +1,171 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <iomanip> +#include <arpa/inet.h> + +#include "pcapwriter.hpp" +#include "stream.hpp" + +Stream::Stream(uint32_t id, uint32_t sizeHint) + : m_id(id), m_prevPktIsClient(false) +{ + m_client.pkts.reserve(sizeHint / 2); + m_server.pkts.reserve(sizeHint / 2); + m_pkts.reserve(sizeHint); +} + +bool Stream::isClient(const PcapPkt &pkt) const +{ + return m_pt == pkt.parsePkt(); +} + +size_t Stream::pktCount() const +{ + return m_client.pkts.size() + m_server.pkts.size(); +} + +void Stream::setTupleFromPkt(const PcapPkt &pkt) +{ + m_pt = pkt.parsePkt(); +} + +void Stream::addPkt(const PcapPkt &pkt) +{ + if (!pktCount()) + setTupleFromPkt(pkt); + + bool isClientPkt = isClient(pkt); + HalfStream *half; + + if (isClientPkt) + half = &m_client; + else + half = &m_server; + + HalfStream::Action::Part p = half->addPkt(pkt); + + if (p.len) { + addAction(half, p, isClientPkt); + } + + m_pkts.push_back(pkt); +} + +void Stream::addAction(HalfStream *half, HalfStream::Action::Part p, bool isClientPkt) +{ + if (m_actions.empty() || m_prevPktIsClient != isClientPkt || m_pt.proto_id == IPPROTO_UDP) + m_actions.push_back(HalfStream::Action(half, p, isClientPkt)); + else + m_actions.back().addPart(p); + m_prevPktIsClient = isClientPkt; +} + +Stream::Header Stream::getHeader() const +{ + Header h; + + h.streamId = m_id; + h.clientHdrLen = m_client.hdrLen; + h.clientContentLen = m_client.contentLen; + h.serverHdrLen = m_server.hdrLen; + h.serverContentLen = m_server.contentLen; + h.actionCount = m_actions.size(); + h.clientIP = m_pt.src_addr; + h.clientPort = m_pt.src_port; + h.serverIP = m_pt.dst_addr; + h.serverPort = m_pt.dst_port; + h.upRate = m_client.getRate(); + h.dnRate = m_server.getRate(); + h.protocol = m_pt.proto_id; + h.completedTCP = (m_client.tcpOpen && m_client.tcpClose && m_server.tcpOpen && m_server.tcpClose) || + (!m_client.tcpOpen && !m_client.tcpClose && !m_server.tcpOpen && !m_server.tcpClose); + + return h; +} + +void Stream::Header::toFile(ofstream *f) const +{ + f->write((const char *)this, sizeof(*this)); +} + +int Stream::Header::fromFile(ifstream *f) +{ + const size_t readSize = sizeof(*this); + + f->read((char *)this, readSize); + return f->gcount() == readSize? 0 : -1; +} + +size_t Stream::Header::getStreamLen() const +{ + return actionCount * sizeof(ActionEntry) + + clientHdrLen + clientContentLen + + serverHdrLen + serverContentLen; +} + +void Stream::actionsToFile(ofstream *f) const +{ + ActionEntry actionEntry; + uint32_t runningTotalLen[2] = {0}; + + for (size_t i = 0; i < m_actions.size(); ++i) { + actionEntry.peer = m_actions[i].isClient()? 0 : 1; + actionEntry.beg = runningTotalLen[actionEntry.peer]; + actionEntry.len = m_actions[i].totLen(); + + runningTotalLen[actionEntry.peer] += actionEntry.len; + f->write((const char *)&actionEntry, sizeof(actionEntry)); + } +} + +void Stream::clientHdrToFile(ofstream *f) const +{ + f->write((const char *)m_client.hdr, m_client.hdrLen); +} + +void Stream::serverHdrToFile(ofstream *f) const +{ + f->write((const char *)m_server.hdr, m_server.hdrLen); +} + +void Stream::contentsToFile(ofstream *f, bool isClient) const +{ + for (size_t i = 0; i < m_actions.size(); ++i) + if (m_actions[i].isClient() == isClient) + m_actions[i].toFile(f); +} + +void Stream::toFile(ofstream *f) +{ + getHeader().toFile(f); + actionsToFile(f); + clientHdrToFile(f); + serverHdrToFile(f); + contentsToFile(f, true); + contentsToFile(f, false); +} + +void Stream::toPcap(const string& outFile) +{ + PcapWriter pw; + + pw.open(outFile); + for (size_t i = 0; i < m_pkts.size(); ++i) + pw.write(m_pkts[i]); + pw.close(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream.hpp b/VNFs/DPPD-PROX/tools/flow_extract/stream.hpp new file mode 100644 index 00000000..28547d18 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream.hpp @@ -0,0 +1,94 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STREAM_H_ +#define _STREAM_H_ + +#include <list> +#include <string> +#include <fstream> +#include <cstring> +#include <vector> +#include <cstdlib> +#include <sys/time.h> + +#include "pcappktref.hpp" +#include "pcappkt.hpp" +#include "netsocket.hpp" +#include "timestamp.hpp" +#include "halfstream.hpp" + +using namespace std; + +class PcapReader; + +class Stream { +public: + struct Header { + uint32_t streamId; + uint16_t clientHdrLen; + uint32_t clientContentLen; + uint16_t serverHdrLen; + uint32_t serverContentLen; + uint32_t actionCount; + uint32_t clientIP; + uint16_t clientPort; + uint32_t serverIP; + uint16_t serverPort; + double upRate; + double dnRate; + uint8_t protocol; + uint8_t completedTCP; + void toFile(ofstream *f) const; + int fromFile(ifstream *f); + size_t getStreamLen() const; + }; + struct ActionEntry { + uint8_t peer; + uint32_t beg; + uint32_t len; + } __attribute__((packed)); + + Stream(uint32_t id = -1, uint32_t sizeHint = 0); + void addPkt(const PcapPkt &pkt); + void toFile(ofstream *f); + void toPcap(const string& outFile); + double getRate() const; + size_t actionCount() const {return m_actions.size();} + +private: + Header getHeader() const; + void actionsToFile(ofstream *f) const; + void clientHdrToFile(ofstream *f) const; + void serverHdrToFile(ofstream *f) const; + void contentsToFile(ofstream *f, bool isClient) const; + bool isClient(const PcapPkt &pkt) const; + size_t pktCount() const; + struct pkt_tuple m_pt; + void setTupleFromPkt(const PcapPkt &pkt); + void addToClient(const PcapPkt &pkt); + void addToServer(const PcapPkt &pkt); + void addAction(HalfStream *half, HalfStream::Action::Part p, bool isClientPkt); + + int m_id; + vector<PcapPkt> m_pkts; + vector<HalfStream::Action> m_actions; + HalfStream m_client; + HalfStream m_server; + bool m_prevPktIsClient; +}; + +#endif /* _STREAM_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream2.cpp b/VNFs/DPPD-PROX/tools/flow_extract/stream2.cpp new file mode 100644 index 00000000..51057e7d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream2.cpp @@ -0,0 +1,151 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iomanip> +#include <arpa/inet.h> +#include <sstream> + +#include "stream.hpp" +#include "stream2.hpp" + +int Stream2::fromFile(ifstream *f) +{ + m_actions.clear(); + if (streamHdr.fromFile(f)) + return -1; + if (actionsFromFile(f, streamHdr.actionCount)) + return -1; + if (setReferences(f)) + return -1; + + return 0; +} + +int Stream2::actionsFromFile(ifstream *f, size_t actionCount) +{ + m_actions.resize(actionCount); + for (size_t i = 0; i < actionCount; ++i) + f->read((char *)&m_actions[i], sizeof(Stream::ActionEntry)); + + return 0; +} + +int Stream2::setReferences(ifstream *f) +{ + size_t toRead = streamHdr.clientHdrLen + + streamHdr.serverHdrLen + + streamHdr.clientContentLen + + streamHdr.serverContentLen; + + delete [] clientServerHdrContent; + clientServerHdrContent = new uint8_t[toRead]; + f->read((char *)clientServerHdrContent, toRead); + return 0; +} + +void Stream2::calcOffsets(ofstream *out) +{ + size_t curPos = out->tellp(); + + clientHdrBeg = curPos; + serverHdrBeg = clientHdrBeg + streamHdr.clientHdrLen; + clientContentBeg = serverHdrBeg + streamHdr.serverHdrLen; + serverContentBeg = clientContentBeg + streamHdr.clientContentLen; +} + +void Stream2::toFile(ofstream *out) const +{ + size_t len = streamHdr.clientHdrLen + + streamHdr.serverHdrLen + + streamHdr.clientContentLen + + streamHdr.serverContentLen; + + out->write((const char *)clientServerHdrContent, len); +} + +static string ipToString(const uint32_t ip) +{ + uint32_t ip_ne = htonl(ip); + stringstream ss; + + ss << ((ip_ne >> 24) & 0xff) << "." + << ((ip_ne >> 16) & 0xff) << "." + << ((ip_ne >> 8) & 0xff) << "." + << (ip_ne & 0xff); + + return ss.str(); +} + +static string spaces(uint32_t count) +{ + stringstream ss; + + while (count--) + ss << " "; + return ss.str(); +} + +NetSocket Stream2::getServerNetSocket() const +{ + return NetSocket(streamHdr.serverIP, ntohs(streamHdr.serverPort)); +} + +NetSocket Stream2::getClientNetSocket() const +{ + return NetSocket(streamHdr.clientIP, ntohs(streamHdr.clientPort)); +} +void Stream2::setServerNetSocket(const NetSocket& netSocket) +{ + streamHdr.serverPort = htons(netSocket.port); + streamHdr.serverIP = netSocket.host; +} + +void Stream2::setClientNetSocket(const NetSocket& netSocket) +{ + streamHdr.clientPort = htons(netSocket.port); + streamHdr.clientIP = netSocket.host; +} +void Stream2::toLua(ofstream *f, const string& binFileName, const string& streamTableName) const + +{ + (*f) << std::fixed; + + (*f) << streamTableName << "[" << streamHdr.streamId << "] = {" << endl + << spaces(3) << "client_data = {" << endl + << spaces(6) << "header = bin_read(" << binFileName << "," << clientHdrBeg << "," << streamHdr.clientHdrLen << "), " << endl + << spaces(6) << "content = bin_read(" << binFileName << "," << clientContentBeg << "," << streamHdr.clientContentLen << "), " << endl + << spaces(3) << "}," << endl + << spaces(3) << "server_data = {" << endl + << spaces(6) << "header = bin_read(" << binFileName << "," << serverHdrBeg << "," << streamHdr.serverHdrLen << "), " << endl + << spaces(6) << "content = bin_read(" << binFileName << "," << serverContentBeg << "," << streamHdr.serverContentLen << "), " << endl + << spaces(3) << "}," << endl + << spaces(3) << "actions = {" << endl; + + for (size_t i = 0; i < m_actions.size(); ++i) { + const char *peer_str = m_actions[i].peer == 0? "client" : "server"; + + (*f) << spaces(6) << peer_str << "_content(" << m_actions[i].beg << "," << m_actions[i].len << ")," << endl; + } + + (*f) << spaces(3) << "}," << endl + << spaces(3) << "clients = {ip = ip(\"" << ipToString(streamHdr.clientIP) << "\"), port = " << ntohs(streamHdr.clientPort) << "}," << endl + << spaces(3) << "servers = {ip = ip(\"" << ipToString(streamHdr.serverIP) << "\"), port = " << ntohs(streamHdr.serverPort) << "}," << endl + << spaces(3) << "l4_proto = \"" << (streamHdr.protocol == 0x06? "tcp" : "udp") << "\"," << endl + << spaces(3) << "up_bps = " << setprecision(4) << streamHdr.upRate << "," << endl + << spaces(3) << "dn_bps = " << setprecision(4) << streamHdr.dnRate << "," << endl; + + (*f) << "}" << endl; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream2.hpp b/VNFs/DPPD-PROX/tools/flow_extract/stream2.hpp new file mode 100644 index 00000000..fd9d9c8c --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream2.hpp @@ -0,0 +1,54 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STREAM2_H_ +#define _STREAM2_H_ + +#include <inttypes.h> +#include <fstream> + +#include "netsocket.hpp" + +using namespace std; + +class Stream2 { +public: + Stream2() : clientServerHdrContent(NULL) {} + ~Stream2() {delete [] clientServerHdrContent;} + int fromFile(ifstream *f); + void calcOffsets(ofstream *out); + void toFile(ofstream *out) const; + void toLua(ofstream *f, const string& binFileName, const string& streamTableName) const; + NetSocket getServerNetSocket() const; + NetSocket getClientNetSocket() const; + void setServerNetSocket(const NetSocket& netSocket); + void setClientNetSocket(const NetSocket& netSocket); + Stream::Header streamHdr; +private: + int actionsFromFile(ifstream *f, size_t actionCount); + int setReferences(ifstream *f); + + uint8_t *clientServerHdrContent; + + uint32_t clientHdrBeg; + uint32_t serverHdrBeg; + uint32_t clientContentBeg; + uint32_t serverContentBeg; + + vector<Stream::ActionEntry> m_actions; +}; + +#endif /* _STREAM2_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream3.cpp b/VNFs/DPPD-PROX/tools/flow_extract/stream3.cpp new file mode 100644 index 00000000..30c166ae --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream3.cpp @@ -0,0 +1,95 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <fstream> + +using namespace std; + +#include "stream3.hpp" + +Stream3::Stream3(uint32_t id, PcapPkt::L4Proto proto) + : m_id(id), m_proto(proto), m_pktCount(0), m_flushCount(0) +{ +} + +void Stream3::writeHeader(ofstream *outputFile) const +{ + outputFile->write(reinterpret_cast<const char *>(&m_id), sizeof(m_id)); + outputFile->write(reinterpret_cast<const char *>(&m_flushCount), sizeof(m_flushCount)); +} + +void Stream3::writePackets(ofstream *outputFile) const +{ + for (size_t i = 0; i < m_pkts.size(); ++i) + m_pkts[i]->toFile(outputFile); +} + +void Stream3::clearPackets() +{ + for (size_t i = 0; i < m_pkts.size(); ++i) + delete m_pkts[i]; + m_pkts.clear(); + m_flushCount = 0; +} + +void Stream3::flush(ofstream *outputFile) +{ + writeHeader(outputFile); + writePackets(outputFile); + clearPackets(); +} + +void Stream3::addPkt(const PcapPkt& pkt) +{ + m_pkts.push_back(new PcapPkt(pkt)); + m_pktCount++; + m_flushCount++; +} + +Timestamp Stream3::getTimeout() const +{ + uint32_t timeoutMinutes = m_proto == PcapPkt::PROTO_UDP? 10 : 5; + + return Timestamp(timeoutMinutes * 60, 0); +} + +uint32_t Stream3::getIDFromMem(uint8_t *mem) +{ + return *reinterpret_cast<uint32_t *>(mem); +} + +void Stream3::addFromMemory(uint8_t *mem, size_t *len) +{ + uint32_t n_pkts; + + mem += sizeof(m_id); + n_pkts = *reinterpret_cast<uint32_t *>(mem); + mem += sizeof(n_pkts); + + *len = sizeof(m_id) + sizeof(n_pkts); + for (uint32_t i = 0; i < n_pkts; ++i) { + addPkt(PcapPkt(mem)); + mem += m_pkts.back()->memSize(); + *len += m_pkts.back()->memSize(); + } +} + +void Stream3::removeAllPackets() +{ + clearPackets(); + m_pktCount = 0; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/stream3.hpp b/VNFs/DPPD-PROX/tools/flow_extract/stream3.hpp new file mode 100644 index 00000000..7e94814e --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/stream3.hpp @@ -0,0 +1,55 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STREAM3_H_ +#define _STREAM3_H_ + +#include <inttypes.h> +#include <vector> + +#include "pcappkt.hpp" +#include "timestamp.hpp" + +using namespace std; +class Allocator; + +class Stream3 { +public: + PcapPkt::L4Proto getProto(void) const {return m_proto;} + Stream3(uint32_t id, PcapPkt::L4Proto proto); + Stream3() : m_id(UINT32_MAX), m_proto(PcapPkt::PROTO_UDP), m_pktCount(0), m_flushCount(0) {} + void addPkt(const PcapPkt& pkt); + void flush(ofstream *outputFile); + void addFromMemory(uint8_t *mem, size_t *len); + static uint32_t getIDFromMem(uint8_t *mem); + bool hasFlushablePackets() const {return !!m_flushCount;} + Timestamp getTimeout() const; + uint32_t getID() const {return m_id;} + void removeAllPackets(); + void setID(const uint32_t id) {m_id = id;} +private: + void writeHeader(ofstream *outputFile) const; + void writePackets(ofstream *outputFile) const; + void clearPackets(); + + uint32_t m_id; + PcapPkt::L4Proto m_proto; + vector<PcapPkt *> m_pkts; + uint32_t m_pktCount; + uint32_t m_flushCount; +}; + +#endif /* _STREAM3_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/streamextract.cpp b/VNFs/DPPD-PROX/tools/flow_extract/streamextract.cpp new file mode 100644 index 00000000..e493ef3f --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/streamextract.cpp @@ -0,0 +1,406 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <inttypes.h> +#include <string> +#include <cstdio> +#include <iostream> +#include <sys/stat.h> +#include <sys/types.h> +#include <sstream> +#include <set> +#include <arpa/inet.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <cerrno> +#include <cstdlib> +#include <map> + +#include "path.hpp" +#include "bundle.hpp" +#include "stream.hpp" +#include "stream2.hpp" +#include "allocator.hpp" +#include "timestamp.hpp" +#include "streamextract.hpp" +#include "pcapreader.hpp" +#include "pcapwriter.hpp" +#include "flowtable.hpp" +#include "stream3.hpp" +#include "netsocket.hpp" +#include "pcappktref.hpp" +#include "progress.hpp" +#include "mappedfile.hpp" +#include "streamsorter.hpp" + +using namespace std; + +static bool is_dir(const string& path_dir_out) +{ + struct stat s = { 0 }; + + if (stat(path_dir_out.c_str(), &s)) { + return false; + } + + return s.st_mode & S_IFDIR; +} + +StreamExtract::StreamExtract(const ProgramConfig &cfg) + : ft2(cfg.flowTableSize), + streamSorter(cfg.flowTableSize, cfg.path_dir_out, 1024UL*1024*1024*8), + cfg(cfg) +{ +} + +vector<Bundle> StreamExtract::createBundles(const string& streamPath) +{ + map<uint32_t, Bundle>::iterator iterBundle; + map<uint32_t, Bundle> bundles; + set<uint32_t> servers; + + Stream2 s; + ifstream binIn; + + binIn.open(streamPath.c_str()); + binIn.seekg(0, binIn.end); + Progress progress(binIn.tellg()); + binIn.seekg(0, binIn.beg); + + while (!s.fromFile(&binIn)) { + if (progress.couldRefresh()) { + progress.setProgress(binIn.tellg()); + progress.refresh(); + } + if (!s.streamHdr.completedTCP) + continue; + if (!s.streamHdr.serverHdrLen) + continue; + /* The current implementation does not support clients + that are also servers. */ + servers.insert(s.streamHdr.serverIP); + if (servers.find(s.streamHdr.clientIP) != servers.end()) + continue; + + /* Since each application is represented as a path + graph (there is only one reply for a given request + and only one request after a given reply), each + application must run on a unique server. For this + reason, check if the socket on the server already + is occupied and if so, keep incrementing the socket + until the collision is resolved. */ + iterBundle = bundles.find(s.streamHdr.clientIP); + + if (iterBundle == bundles.end()) { + bundles.insert(make_pair(s.streamHdr.clientIP, Bundle())); + iterBundle = bundles.find(s.streamHdr.clientIP); + } + + (*iterBundle).second.addStream(s.streamHdr.streamId, s.getServerNetSocket().port); + } + + progress.setProgress(); + progress.refresh(true); + + binIn.close(); + + vector<Bundle> ret; + + ret.reserve(bundles.size()); + + for (map<uint32_t, Bundle>::const_iterator i = bundles.begin(); i != bundles.end(); ++i) + ret.push_back(i->second); + + return ret; +} + +set<uint32_t> StreamExtract::getBundleStreamIDs(const vector<Bundle*>& bundleSamples) +{ + set<uint32_t> streamIDs; + + for (size_t i = 0; i < bundleSamples.size(); ++i) { + const vector<uint32_t> &bundleStreamIDs = bundleSamples[i]->getStream(); + + for (vector<uint32_t>::const_iterator j = bundleStreamIDs.begin(); j != bundleStreamIDs.end(); ++j) { + streamIDs.insert(*j); + } + } + + return streamIDs; +} + +static size_t getRandom(size_t limit) +{ + size_t r = rand(); + size_t rand_limit = (RAND_MAX/limit)*limit; + + while (r > rand_limit) + r = rand(); + + return r % limit; +} + +static void removeFill(vector<Bundle*> *from, size_t idx) +{ + Bundle *last = from->back(); + from->pop_back(); + + if (idx != from->size()) + (*from)[idx] = last; +} + +static vector<Bundle*> takeSamples(vector<Bundle>& bundles, size_t sampleCount) +{ + vector<Bundle*> bundleSamples; + + bundleSamples.reserve(bundles.size()); + + cout << "Sampling " << sampleCount << " bundles out of " << bundles.size() << endl; + for (size_t i = 0; i < bundles.size(); ++i) + bundleSamples.push_back(&bundles[i]); + + srand(1000); + while (bundleSamples.size() > sampleCount) { + size_t r = getRandom(bundleSamples.size()); + removeFill(&bundleSamples, r); + } + return bundleSamples; +} + +static size_t replaceWithRunningTotals(vector<size_t> *streamLength) +{ + size_t runningTotal = 0; + for (size_t i = 0; i < streamLength->size(); ++i) { + size_t len = (*streamLength)[i] + sizeof(uint32_t); + (*streamLength)[i] = runningTotal; + runningTotal += len; + } + return runningTotal; +} + +static void printPorts(const vector<Bundle> &bundles) +{ + set<uint32_t> streamIDs; + + for (size_t i = 0; i < bundles.size(); ++i) { + const vector<uint32_t> &ports = bundles[i].getPorts(); + + for (size_t j = 0; j < ports.size(); ++j) { + if (j + 1 == ports.size()) + cout << ports[j] << ",END" << endl; + else + cout << ports[j] << "," << ports[j +1] << endl; + } + } +} + +string StreamExtract::createStreamPcapFileName(int id) +{ + stringstream ss; + + ss << cfg.path_dir_out << "/s" << id << ".pcap"; + + return ss.str(); +} + +int StreamExtract::writeToPcaps(const string &sourceFilePath, const set<uint32_t> &streamIDs) +{ + set<uint32_t>::const_iterator i = streamIDs.begin(); + + MappedFile mappedFile; + if (mappedFile.open(sourceFilePath)) { + cerr << "Failed to open file " << sourceFilePath << ":" << strerror(errno) << endl; + return -1; + } + + PcapPkt::allocator = NULL; + + Progress progress((uint64_t)mappedFile.getMapEnd() - (uint64_t)mappedFile.getMapBeg()); + cout << "Writing " << streamIDs.size() << " streams to pcaps" << endl; + uint8_t *data2 = mappedFile.getMapBeg(); + while (data2 < mappedFile.getMapEnd()) { + uint32_t id = *reinterpret_cast<uint32_t *>(data2); + + data2 += sizeof(id); + uint32_t pktCount = *reinterpret_cast<uint32_t *>(data2); + data2 += sizeof(pktCount); + Stream s(id, pktCount); + while (pktCount--) { + PcapPkt p(data2); + + data2 += p.memSize(); + s.addPkt(p); + } + + while (i != streamIDs.end() && (*i) < id) + i++; + if (i == streamIDs.end()) + break; + if (*i > id) + continue; + + const string pcapPath = createStreamPcapFileName(id); + + s.toPcap(pcapPath); + if (progress.couldRefresh()) { + progress.setProgress((uint64_t)data2 - (uint64_t)mappedFile.getMapBeg()); + progress.refresh(); + mappedFile.sync(); + } + } + + progress.setProgress(data2 - mappedFile.getMapBeg()); + progress.refresh(true); + + mappedFile.close(); + return 0; +} + +int StreamExtract::writeToLua(const string& binFilePath, const Path &smallFinalBin, const string& luaFilePath, const string &orderedTemp) +{ + vector<Bundle> bundles = createBundles(binFilePath); + vector<Bundle*> bundleSamples = takeSamples(bundles, cfg.sampleCount); + set<uint32_t> streamIDs = getBundleStreamIDs(bundleSamples); + + if (cfg.write_pcaps) + writeToPcaps(orderedTemp, streamIDs); + + ofstream outLua; + ofstream outSmallBin; + outLua.open(luaFilePath.c_str()); + outLua << "bf = \""<< smallFinalBin.getFileName() << "\"" << endl; + outLua << "s = {}\n"; + set<uint32_t>::iterator i = streamIDs.begin(); + + set<NetSocket> serverSockets; + ifstream binIn; + Stream2 s; + + outSmallBin.open(smallFinalBin.str().c_str()); + binIn.open(binFilePath.c_str()); + while (!s.fromFile(&binIn)) { + while (i != streamIDs.end() && (*i) < s.streamHdr.streamId) + i++; + if (i == streamIDs.end()) + break; + if (*i > s.streamHdr.streamId) + continue; + s.calcOffsets(&outSmallBin); + s.toFile(&outSmallBin); + while (serverSockets.find(s.getServerNetSocket()) != serverSockets.end()) { + NetSocket ns = s.getServerNetSocket(); + + ns.port++; + s.setServerNetSocket(ns); + } + serverSockets.insert(s.getServerNetSocket()); + + s.toLua(&outLua, "bf", "s"); + } + binIn.close(); + + uint32_t bundleCount = 0; + + outLua << "bundles = {}" << endl; + for (size_t i = 0; i < bundleSamples.size(); ++i) { + bundleSamples[i]->toLua(&outLua, "s", ++bundleCount); + } + outLua << "return bundles" << endl; + outLua.close(); + return 0; +} + +int StreamExtract::writeFinalBin(const string& sourceFilePath, const string& destFilePath) +{ + MappedFile mappedFile; + if (mappedFile.open(sourceFilePath)) { + cerr << "Failed to open file " << sourceFilePath << ":" << strerror(errno) << endl; + return -1; + } + ofstream binOut; + + binOut.open(destFilePath.c_str()); + PcapPkt::allocator = NULL; + + Progress progress((uint64_t)mappedFile.getMapEnd() - (uint64_t)mappedFile.getMapBeg()); + + int streamCount = 0; + uint8_t *data2 = mappedFile.getMapBeg(); + while (data2 < mappedFile.getMapEnd()) { + uint32_t id = *reinterpret_cast<uint32_t *>(data2); + + data2 += sizeof(id); + uint32_t pktCount = *reinterpret_cast<uint32_t *>(data2); + data2 += sizeof(pktCount); + Stream s(id, pktCount); + while (pktCount--) { + PcapPkt p(data2); + + data2 += p.memSize(); + s.addPkt(p); + } + s.toFile(&binOut); + streamCount++; + if (progress.couldRefresh()) { + progress.setProgress((uint64_t)data2 - (uint64_t)mappedFile.getMapBeg()); + progress.refresh(); + mappedFile.sync(); + } + } + + progress.setProgress(data2 - mappedFile.getMapBeg()); + progress.refresh(true); + + binOut.close(); + mappedFile.close(); + return 0; +} + +int StreamExtract::run() +{ + Path p(cfg.path_dir_out); + p.mkdir(); + + string orderedTemp = p.add("/a").str(); + + string finalBin = p.add("/b").str(); + Path smallfinalBin = p.add("/data.bin").str(); + string luaFile = p.add("/cfg.lua").str(); + + cout << "Writing to directory '" << p.str() << "'" << endl; + cout << "Ordered streams '" << orderedTemp << "'" << endl; + cout << "Final binary output '" << finalBin << "'" << endl; + cout << "lua file '" << luaFile << "' will contain " << cfg.sampleCount << " bundles" << endl; + + if (cfg.run_first_step) { + cout << "starting sorting" << endl; + streamSorter.sort(cfg.path_file_in_pcap, orderedTemp); + cout << "writing final binary file (converting format)" << endl; + if (writeFinalBin(orderedTemp, finalBin)) + return -1; + } else { + cout << "Skipping first step" << endl; + if (!Path(finalBin).isFile()) { + cerr << "File is missing:" << finalBin << endl; + return -1; + } + } + cout << "writing Lua '" << luaFile << "'" << endl; + if (writeToLua(finalBin, smallfinalBin, luaFile, orderedTemp)) + return -1; + return 0; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/streamextract.hpp b/VNFs/DPPD-PROX/tools/flow_extract/streamextract.hpp new file mode 100644 index 00000000..d5dbdb05 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/streamextract.hpp @@ -0,0 +1,55 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STREAMEXTRACT_H_ +#define _STREAMEXTRACT_H_ + +#include <string> +#include <list> +#include <map> +#include <set> + +#include "programconfig.hpp" +#include "bundle.hpp" +#include "pcapreader.hpp" +#include "flowtable.hpp" +#include "pcappkt.hpp" +#include "stream3.hpp" +#include "streamsorter.hpp" +#include "path.hpp" + +using namespace std; + +class StreamExtract { +public: + /* The size of the flow table determines the number of flows + that can be active at a given time. When a flow expires, it + is written out to a file and the memory is freed. */ + StreamExtract(const ProgramConfig &cfg); + int run(); +private: + int writeToPcaps(const string &sourceFilePath, const set<uint32_t> &streamIDs); + int writeToLua(const string& binFilePath, const Path &smallFinalBin, const string& luaFilePath, const string& orderedTemp); + int writeFinalBin(const string& sourceFilePath, const string& destFilePath); + string createStreamPcapFileName(int id); + vector<Bundle> createBundles(const string& streamPath); + set<uint32_t> getBundleStreamIDs(const vector<Bundle*>& bundleSamples); + FlowTable<pkt_tuple, Stream3> ft2; + StreamSorter streamSorter; + ProgramConfig cfg; +}; + +#endif /* _STREAMEXTRACT_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.cpp b/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.cpp new file mode 100644 index 00000000..65c645e1 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.cpp @@ -0,0 +1,203 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <iostream> +#include <fstream> +#include <cstdlib> + +#include "mappedfile.hpp" +#include "memreader.hpp" +#include "streamsorter.hpp" +#include "path.hpp" +#include "allocator.hpp" +#include "pcapreader.hpp" +#include "progress.hpp" + +StreamSorter::StreamSorter(size_t flowTableSize, const string& workingDirectory, size_t memoryLimit) + : flowTableSize(flowTableSize), + workingDirectory(workingDirectory), + allocator(memoryLimit, 1024*10), + streamID(0) +{ +} + +void StreamSorter::sort(const string &inputPcapFilePath, const string &outputBinFilePath) +{ + setTempFileName(); + sortChunks(inputPcapFilePath); + mergeChunks(outputBinFilePath); +} + +void StreamSorter::sortChunks(const string &inputPcapFilePath) +{ + ofstream outputTempFile; + + outputTempFile.open(tempFilePath.c_str()); + + if (!outputTempFile.is_open()) + return ; + + PcapReader pr; + PcapPkt pkt; + + if (pr.open(inputPcapFilePath)) { + pr.getError(); + return; + } + PcapPkt::allocator = &allocator; + + Progress progress(pr.end()); + uint32_t packetDetail = progress.addDetail("packet count"); + + ft = new FlowTable<pkt_tuple, uint32_t>(flowTableSize); + resetStreams(); + + while (pr.read(&pkt)) { + processPkt(pkt); + if (progress.couldRefresh()) { + progress.setProgress(pr.pos()); + progress.setDetail(packetDetail, pr.getPktReadCount()); + progress.refresh(); + } + if (allocator.lowThresholdReached()) { + flushStreams(&outputTempFile); + } + } + progress.setProgress(); + progress.setDetail(packetDetail, pr.getPktReadCount()); + progress.refresh(true); + + pr.close(); + flushStreams(&outputTempFile); + PcapPkt::allocator = NULL; + outputTempFile.close(); + delete ft; +} + +void StreamSorter::resetStreams() +{ + streams.clear(); +} + +void StreamSorter::flushStreams(ofstream *outputTempFile) +{ + size_t flushCount = 0; + size_t offset = outputTempFile->tellp(); + + Progress progress(streams.size()); + + cout << endl; + progress.setTitle("flush "); + for (size_t i = 0; i < streams.size(); ++i) { + if (streams[i].hasFlushablePackets()) { + streams[i].flush(outputTempFile); + flushCount++; + } + + if (progress.couldRefresh()) { + progress.setProgress(i); + progress.refresh(); + } + } + progress.setProgress(); + progress.refresh(true); + + if (flushCount) + flushOffsets.push_back(offset); + allocator.reset(); +} + +Stream3 *StreamSorter::addNewStream(PcapPkt::L4Proto proto) +{ + streams.push_back(Stream3(streamID++, proto)); + return &streams.back(); +} + +FlowTable<pkt_tuple, uint32_t>::entry* StreamSorter::getFlowEntry(const PcapPkt &pkt) +{ + FlowTable<pkt_tuple, uint32_t>::entry *a; + struct pkt_tuple pt = pkt.parsePkt(); + Stream3 *stream = NULL; + + a = ft->lookup(pt.flip()); + if (!a) { + a = ft->lookup(pt); + if (!a) { + stream = addNewStream(pkt.getProto()); + + a = ft->insert(pt, stream->getID(), pkt.ts()); + } + } + + if (a->expired(pkt.ts(), streams[a->value].getTimeout())) { + ft->remove(a); + + stream = addNewStream(pkt.getProto()); + + a = ft->insert(pt, stream->getID(), pkt.ts()); + } + return a; +} + +void StreamSorter::processPkt(const PcapPkt &pkt) +{ + FlowTable<pkt_tuple, uint32_t>::entry *a; + + a = getFlowEntry(pkt); + a->tv = pkt.ts(); + streams[a->value].addPkt(pkt); +} + +void StreamSorter::mergeChunks(const string &outputBinFile) +{ + cout << "merging chunks: " << tempFilePath << " to " << outputBinFile << endl; + cout << "have " << flushOffsets.size() << " parts to merge" << endl; + MappedFile tempFile; + + if (tempFile.open(tempFilePath)) { + cerr << "failed to open temp file" << endl; + return; + } + ofstream file; + + file.open(outputBinFile.c_str()); + + if (!file.is_open()) { + cerr << "failed top open file '" << outputBinFile << "'" << endl; + return; + } + MemReader memReader(&tempFile, flushOffsets); + Stream3 stream; + + Progress progress(memReader.getTotalLength()); + + while (memReader.read(&stream)) { + stream.flush(&file); + if (progress.couldRefresh()) { + progress.setProgress(memReader.consumed()); + progress.refresh(); + } + } + + progress.setProgress(); + progress.refresh(true); + tempFile.close(); +} + +void StreamSorter::setTempFileName() +{ + tempFilePath = Path(workingDirectory).add("/tmp").str(); +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.hpp b/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.hpp new file mode 100644 index 00000000..a6d3d6cd --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/streamsorter.hpp @@ -0,0 +1,47 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _STREAMSORTER_H_ +#define _STREAMSORTER_H_ + +#include "stream3.hpp" +#include "flowtable.hpp" +#include "allocator.hpp" + +class StreamSorter { +public: + StreamSorter(size_t flowTableSize, const string& workingDirectory, size_t memoryLimit); + void sort(const string &inputPcapFile, const string &outputBinFile); +private: + void sortChunks(const string &inputPcapFilePath); + void mergeChunks(const string &outputBinFilePath); + void setTempFileName(); + void processPkt(const PcapPkt &pkt); + void resetStreams(); + FlowTable<pkt_tuple, uint32_t>::entry* getFlowEntry(const PcapPkt &pkt); + void flushStreams(ofstream *outputTempFile); + Stream3 *addNewStream(PcapPkt::L4Proto proto); + size_t flowTableSize; + FlowTable<pkt_tuple, uint32_t> *ft; + vector<size_t> flushOffsets; + vector<Stream3> streams; + string tempFilePath; + const string workingDirectory; + Allocator allocator; + uint32_t streamID; +}; + +#endif /* _STREAMSORTER_H_ */ diff --git a/VNFs/DPPD-PROX/tools/flow_extract/timestamp.cpp b/VNFs/DPPD-PROX/tools/flow_extract/timestamp.cpp new file mode 100644 index 00000000..9e91173d --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/timestamp.cpp @@ -0,0 +1,65 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <cstdio> +#include <iostream> +#include <iomanip> + +#include "timestamp.hpp" + +Timestamp Timestamp::operator-(const Timestamp& other) const +{ + uint64_t sec; + uint64_t nsec; + + if (other.m_nsec <= m_nsec) { + nsec = m_nsec - other.m_nsec; + sec = m_sec - other.m_sec; + } else { + nsec = (1000000000 + m_nsec) - other.m_nsec; + sec = m_sec - 1 - other.m_sec; + } + + return Timestamp(sec, nsec); +} + +bool Timestamp::operator>(const Timestamp& other) +{ + return m_sec > other.m_sec || + (m_sec == other.m_sec && m_nsec > other.m_nsec); +} + +bool Timestamp::operator<(const Timestamp& other) +{ + return m_sec < other.m_sec || + (m_sec == other.m_sec && m_nsec < other.m_nsec); +} + +ostream& operator<<(ostream& stream, const Timestamp& ts) +{ + stream << ts.m_sec << "." << setw(9) << setfill('0') << ts.m_nsec; + return stream; +} + +double operator/(double d, const Timestamp &denominator) +{ + return d * 1000000000 / (denominator.m_sec * 1000000000 + denominator.m_nsec); +} + +bool Timestamp::operator==(const Timestamp &other) const +{ + return m_sec == other.m_sec && m_nsec == other.m_nsec; +} diff --git a/VNFs/DPPD-PROX/tools/flow_extract/timestamp.hpp b/VNFs/DPPD-PROX/tools/flow_extract/timestamp.hpp new file mode 100644 index 00000000..cf8ec5d4 --- /dev/null +++ b/VNFs/DPPD-PROX/tools/flow_extract/timestamp.hpp @@ -0,0 +1,45 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TIMESTAMP_H_ +#define _TIMESTAMP_H_ + +#include <iostream> + +#include <sys/time.h> +#include <inttypes.h> + +using namespace std; + +class Timestamp { +public: + Timestamp(const uint64_t sec, const uint64_t nsec) : m_sec(sec), m_nsec(nsec) {} + Timestamp() {} + Timestamp(const struct timeval& tv) : m_sec(tv.tv_sec), m_nsec(tv.tv_usec) {} + Timestamp operator-(const Timestamp& other) const; + bool operator==(const Timestamp &other) const; + friend double operator/(double d, const Timestamp &denominator); + bool operator>(const Timestamp& other); + bool operator<(const Timestamp& other); + uint64_t sec() const {return m_sec;} + uint64_t nsec() const {return m_nsec;} + friend ostream& operator<<(ostream& stream, const Timestamp& ts); +private: + uint64_t m_sec; + uint64_t m_nsec; +}; + +#endif /* _TIMESTAMP_H_ */ diff --git a/VNFs/DPPD-PROX/tx_pkt.c b/VNFs/DPPD-PROX/tx_pkt.c new file mode 100644 index 00000000..c6f6010c --- /dev/null +++ b/VNFs/DPPD-PROX/tx_pkt.c @@ -0,0 +1,665 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <rte_ethdev.h> +#include <rte_version.h> + +#include "rx_pkt.h" +#include "tx_pkt.h" +#include "task_base.h" +#include "stats.h" +#include "prefetch.h" +#include "prox_assert.h" +#include "log.h" +#include "mbuf_utils.h" + +static void buf_pkt_single(struct task_base *tbase, struct rte_mbuf *mbuf, const uint8_t out) +{ + const uint16_t prod = tbase->ws_mbuf->idx[out].prod++; + tbase->ws_mbuf->mbuf[out][prod & WS_MBUF_MASK] = mbuf; +} + +static inline void buf_pkt_all(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + if (unlikely(out[j] >= OUT_HANDLED)) { + rte_pktmbuf_free(mbufs[j]); + if (out[j] == OUT_HANDLED) + TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, 1); + else + TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, 1); + } + else { + buf_pkt_single(tbase, mbufs[j], out[j]); + } + } +} +#define MAX_PMD_TX 32 + +/* The following help functions also report stats. Therefore we need + to pass the task_base struct. */ +static inline int txhw_drop(const struct port_queue *port_queue, struct rte_mbuf **mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase) +{ + uint16_t ntx; + int ret; + + /* TX vector mode can't transmit more than 32 packets */ + if (n_pkts > MAX_PMD_TX) { + ntx = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, MAX_PMD_TX); + ntx += rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs + ntx, n_pkts - ntx); + } else { + ntx = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, n_pkts); + } + + TASK_STATS_ADD_TX(&tbase->aux->stats, ntx); + ret = n_pkts - ntx; + if (ntx < n_pkts) { + TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts - ntx); + if (tbase->tx_pkt == tx_pkt_bw) { + uint32_t drop_bytes = 0; + do { + drop_bytes += mbuf_wire_size(mbufs[ntx]); + rte_pktmbuf_free(mbufs[ntx++]); + } while (ntx < n_pkts); + TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes); + } + else { + do { + rte_pktmbuf_free(mbufs[ntx++]); + } while (ntx < n_pkts); + } + } + return ret; +} + +static inline int txhw_no_drop(const struct port_queue *port_queue, struct rte_mbuf **mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase) +{ + uint16_t ret; + uint16_t n = n_pkts; + + TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts); + + do { + ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, n_pkts); + mbufs += ret; + n_pkts -= ret; + } + while (n_pkts); + return (n != ret); +} + +static inline int ring_enq_drop(struct rte_ring *ring, struct rte_mbuf *const *mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase) +{ + int ret = 0; + /* return 0 on succes, -ENOBUFS on failure */ + // Rings can be single or multiproducer (ctrl rings are multi producer) +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + if (unlikely(rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts))) { +#else + if (unlikely(rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts, NULL) == 0)) { +#endif + ret = n_pkts; + if (tbase->tx_pkt == tx_pkt_bw) { + uint32_t drop_bytes = 0; + for (uint16_t i = 0; i < n_pkts; ++i) { + drop_bytes += mbuf_wire_size(mbufs[i]); + rte_pktmbuf_free(mbufs[i]); + } + TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes); + TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts); + } + else { + for (uint16_t i = 0; i < n_pkts; ++i) + rte_pktmbuf_free(mbufs[i]); + TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts); + } + } + else { + TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts); + } + return ret; +} + +static inline int ring_enq_no_drop(struct rte_ring *ring, struct rte_mbuf *const *mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase) +{ + int i = 0; +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + while (rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts)) { +#else + while (rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts, NULL) == 0) { +#endif + i++; + }; + TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts); + return (i != 0); +} + +void flush_queues_hw(struct task_base *tbase) +{ + uint16_t prod, cons; + + for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (prod != cons) { + tbase->ws_mbuf->idx[i].prod = 0; + tbase->ws_mbuf->idx[i].cons = 0; + txhw_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase); + } + } + + tbase->flags &= ~FLAG_TX_FLUSH; +} + +void flush_queues_sw(struct task_base *tbase) +{ + uint16_t prod, cons; + + for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (prod != cons) { + tbase->ws_mbuf->idx[i].prod = 0; + tbase->ws_mbuf->idx[i].cons = 0; + ring_enq_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase); + } + } + tbase->flags &= ~FLAG_TX_FLUSH; +} + +void flush_queues_no_drop_hw(struct task_base *tbase) +{ + uint16_t prod, cons; + + for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (prod != cons) { + tbase->ws_mbuf->idx[i].prod = 0; + tbase->ws_mbuf->idx[i].cons = 0; + txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase); + } + } + + tbase->flags &= ~FLAG_TX_FLUSH; +} + +void flush_queues_no_drop_sw(struct task_base *tbase) +{ + uint16_t prod, cons; + + for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (prod != cons) { + tbase->ws_mbuf->idx[i].prod = 0; + tbase->ws_mbuf->idx[i].cons = 0; + ring_enq_no_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase); + } + } + tbase->flags &= ~FLAG_TX_FLUSH; +} + +/* "try" functions try to send packets to sw/hw w/o failing or blocking; + They return if ring/queue is full and are used by aggregators. + "try" functions do not have drop/no drop flavors + They are only implemented in never_discard mode (as by default they + use only one outgoing ring. */ +uint16_t tx_try_self(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + if (n_pkts < 64) { + tx_pkt_never_discard_self(tbase, mbufs, n_pkts, NULL); + return n_pkts; + } else { + tx_pkt_never_discard_self(tbase, mbufs, 64, NULL); + return 64; + } +} + +uint16_t tx_try_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + const int bulk_size = 64; + uint16_t ret = bulk_size, sent = 0, n_bulks; + n_bulks = n_pkts >> __builtin_ctz(bulk_size); + + for (int i = 0; i < n_bulks; i++) { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, bulk_size); +#else + ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, bulk_size, NULL); +#endif + mbufs += ret; + sent += ret; + if (ret != bulk_size) + break; + } + if ((ret == bulk_size) && (n_pkts & (bulk_size - 1))) { +#if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1) + ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, (n_pkts & (bulk_size - 1))); +#else + ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, (n_pkts & (bulk_size - 1)), NULL); +#endif + mbufs += ret; + sent += ret; + } + TASK_STATS_ADD_TX(&tbase->aux->stats, sent); + return sent; +} + +uint16_t tx_try_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + const struct port_queue *port_queue = &tbase->tx_params_hw.tx_port_queue[0]; + const int bulk_size = 64; + uint16_t ret = bulk_size, n_bulks, sent = 0; + n_bulks = n_pkts >> __builtin_ctz(bulk_size); + + for (int i = 0; i < n_bulks; i++) { + ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, bulk_size); + mbufs += ret; + sent += ret; + if (ret != bulk_size) + break; + } + if ((ret == bulk_size) && (n_pkts & (bulk_size - 1))) { + ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, (n_pkts & (bulk_size - 1))); + mbufs += ret; + sent += ret; + } + TASK_STATS_ADD_TX(&tbase->aux->stats, sent); + return sent; +} + +int tx_pkt_no_drop_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + return txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase); +} + +int tx_pkt_no_drop_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + static uint8_t fake_out[MAX_PKT_BURST] = {0}; + int ret = 0; + if (n_pkts == MAX_PKT_BURST) { + // First xmit what was queued + uint16_t prod, cons; + + prod = tbase->ws_mbuf->idx[0].prod; + cons = tbase->ws_mbuf->idx[0].cons; + + if ((uint16_t)(prod - cons)){ + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[0].prod = 0; + tbase->ws_mbuf->idx[0].cons = 0; + ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], tbase->ws_mbuf->mbuf[0] + (cons & WS_MBUF_MASK), (uint16_t)(prod - cons), tbase); + } + ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase); + } else { + ret+= tx_pkt_no_drop_hw(tbase, mbufs, n_pkts, fake_out); + } + return ret; +} + +int tx_pkt_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + return txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase); +} + +int tx_pkt_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + static uint8_t fake_out[MAX_PKT_BURST] = {0}; + int ret = 0; + if (n_pkts == MAX_PKT_BURST) { + // First xmit what was queued + uint16_t prod, cons; + + prod = tbase->ws_mbuf->idx[0].prod; + cons = tbase->ws_mbuf->idx[0].cons; + + if ((uint16_t)(prod - cons)){ + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[0].prod = 0; + tbase->ws_mbuf->idx[0].cons = 0; + ret+= txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], tbase->ws_mbuf->mbuf[0] + (cons & WS_MBUF_MASK), (uint16_t)(prod - cons), tbase); + } + ret+= txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase); + } else { + ret+= tx_pkt_hw(tbase, mbufs, n_pkts, fake_out); + } + return ret; +} + +/* Transmit to hw using tx_params_hw_sw structure + This function is used to transmit to hw when tx_params_hw_sw should be used + i.e. when the task needs to transmit both to hw and sw */ +int tx_pkt_no_drop_never_discard_hw1_no_pointer(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + txhw_no_drop(&tbase->tx_params_hw_sw.tx_port_queue, mbufs, n_pkts, tbase); + return 0; +} + +int tx_pkt_no_drop_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + return ring_enq_no_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_pkts, tbase); +} + +int tx_pkt_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + return ring_enq_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_pkts, tbase); +} + +static uint16_t tx_pkt_free_dropped(__attribute__((unused)) struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + uint64_t v = 0; + uint16_t i; + /* The most probable and most important optimize case is if + the no packets should be dropped. */ + for (i = 0; i + 8 < n_pkts; i += 8) { + v |= *((uint64_t*)(&out[i])); + } + for (; i < n_pkts; ++i) { + v |= out[i]; + } + + if (unlikely(v)) { + /* At least some packets need to be dropped, so the + mbufs array needs to be updated. */ + uint16_t n_kept = 0; + uint16_t n_discard = 0; + for (uint16_t i = 0; i < n_pkts; ++i) { + if (unlikely(out[i] >= OUT_HANDLED)) { + rte_pktmbuf_free(mbufs[i]); + n_discard += out[i] == OUT_DISCARD; + continue; + } + mbufs[n_kept++] = mbufs[i]; + } + TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, n_discard); + TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, n_pkts - n_kept - n_discard); + return n_kept; + } + return n_pkts; +} + +int tx_pkt_no_drop_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out); + int ret = 0; + + if (likely(n_kept)) + ret = txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_kept, tbase); + return ret; +} + +int tx_pkt_no_drop_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out); + int ret = 0; + + if (likely(n_kept)) + ret = ring_enq_no_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_kept, tbase); + return ret; +} + +int tx_pkt_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out); + + if (likely(n_kept)) + return txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_kept, tbase); + return n_pkts; +} + +int tx_pkt_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out); + + if (likely(n_kept)) + return ring_enq_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_kept, tbase); + return 0; +} + +int tx_pkt_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out) +{ + const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out); + + TASK_STATS_ADD_TX(&tbase->aux->stats, n_kept); + tbase->ws_mbuf->idx[0].nb_rx = n_kept; + struct rte_mbuf **tx_mbuf = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + for (uint16_t i = 0; i < n_kept; ++i) { + tx_mbuf[i] = mbufs[i]; + } + return 0; +} + +int tx_pkt_never_discard_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out) +{ + TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts); + tbase->ws_mbuf->idx[0].nb_rx = n_pkts; + struct rte_mbuf **tx_mbuf = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK); + for (uint16_t i = 0; i < n_pkts; ++i) { + tx_mbuf[i] = mbufs[i]; + } + return 0; +} + +int tx_pkt_no_drop_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + int ret = 0; + buf_pkt_all(tbase, mbufs, n_pkts, out); + + const uint8_t nb_bufs = tbase->tx_params_hw.nb_txports; + uint16_t prod, cons; + + for (uint8_t i = 0; i < nb_bufs; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) { + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST; + ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase); + } + } + return ret; +} + +int tx_pkt_no_drop_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + int ret = 0; + buf_pkt_all(tbase, mbufs, n_pkts, out); + + const uint8_t nb_bufs = tbase->tx_params_sw.nb_txrings; + uint16_t prod, cons; + + for (uint8_t i = 0; i < nb_bufs; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) { + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST; + ret += ring_enq_no_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase); + } + } + return ret; +} + +int tx_pkt_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + int ret = 0; + buf_pkt_all(tbase, mbufs, n_pkts, out); + + const uint8_t nb_bufs = tbase->tx_params_hw.nb_txports; + uint16_t prod, cons; + + for (uint8_t i = 0; i < nb_bufs; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) { + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST; + ret += txhw_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase); + } + } + return ret; +} + +int tx_pkt_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + int ret = 0; + buf_pkt_all(tbase, mbufs, n_pkts, out); + + const uint8_t nb_bufs = tbase->tx_params_sw.nb_txrings; + uint16_t prod, cons; + for (uint8_t i = 0; i < nb_bufs; ++i) { + prod = tbase->ws_mbuf->idx[i].prod; + cons = tbase->ws_mbuf->idx[i].cons; + + if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) { + tbase->flags &= ~FLAG_TX_FLUSH; + tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST; + ret+= ring_enq_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase); + } + } + return ret; +} + +int tx_pkt_trace(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + int ret = 0; + if (tbase->aux->task_rt_dump.cur_trace == 0) { + // No packet received since dumping... + // So the transmitted packets should not be linked to received packets + tbase->aux->task_rt_dump.n_print_tx = tbase->aux->task_rt_dump.n_trace; + tbase->aux->task_rt_dump.n_trace = 0; + task_base_del_rx_pkt_function(tbase, rx_pkt_trace); + return tx_pkt_dump(tbase, mbufs, n_pkts, out); + } + plog_info("Tracing %d pkts\n", tbase->aux->task_rt_dump.cur_trace); + + for (uint32_t i = 0; i < tbase->aux->task_rt_dump.cur_trace; ++i) { + struct rte_mbuf tmp; + /* For each packet being transmitted, find which + buffer represent the packet as it was before + processing. */ + uint32_t j = 0; + uint32_t len = sizeof(tbase->aux->task_rt_dump.pkt_mbuf_addr)/sizeof(tbase->aux->task_rt_dump.pkt_mbuf_addr[0]); + for (;j < len; ++j) { + if (tbase->aux->task_rt_dump.pkt_mbuf_addr[j] == mbufs[i]) + break; + } + if (j == len) { + plog_info("Trace RX: missing!\n"); + } + else { +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + tmp.data_off = 0; +#endif + rte_pktmbuf_data_len(&tmp) = tbase->aux->task_rt_dump.pkt_cpy_len[j]; + rte_pktmbuf_pkt_len(&tmp) = tbase->aux->task_rt_dump.pkt_cpy_len[j]; + tmp.buf_addr = tbase->aux->task_rt_dump.pkt_cpy[j]; + plogd_info(&tmp, "Trace RX: "); + } + + if (out) { + if (out[i] != 0xFF) + plogd_info(mbufs[i], "Trace TX[%d]: ", out[i]); + else + plogd_info(mbufs[i], "Trace Dropped: "); + } else + plogd_info(mbufs[i], "Trace TX: "); + } + ret = tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out); + + /* Unset by TX when n_trace = 0 */ + if (0 == tbase->aux->task_rt_dump.n_trace) { + tbase->tx_pkt = tbase->aux->tx_pkt_orig; + tbase->aux->tx_pkt_orig = NULL; + task_base_del_rx_pkt_function(tbase, rx_pkt_trace); + } + return ret; +} + +int tx_pkt_dump(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + uint32_t n_dump = tbase->aux->task_rt_dump.n_print_tx; + int ret = 0; + + n_dump = n_pkts < n_dump? n_pkts : n_dump; + for (uint32_t i = 0; i < n_dump; ++i) { + if (out) + plogd_info(mbufs[i], "TX[%d]: ", out[i]); + else + plogd_info(mbufs[i], "TX: "); + } + tbase->aux->task_rt_dump.n_print_tx -= n_dump; + + ret = tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out); + + if (0 == tbase->aux->task_rt_dump.n_print_tx) { + tbase->tx_pkt = tbase->aux->tx_pkt_orig; + tbase->aux->tx_pkt_orig = NULL; + } + return ret; +} + +/* Gather the distribution of the number of packets that have been + xmitted from one TX call. Since the value is only modified by the + task that xmits the packet, no atomic operation is needed. */ +int tx_pkt_distr(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + tbase->aux->tx_bucket[n_pkts]++; + return tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out); +} + +int tx_pkt_bw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + uint32_t tx_bytes = 0; + uint32_t drop_bytes = 0; + + for (uint16_t i = 0; i < n_pkts; ++i) { + if (!out || out[i] < OUT_HANDLED) + tx_bytes += mbuf_wire_size(mbufs[i]); + else + drop_bytes += mbuf_wire_size(mbufs[i]); + } + + TASK_STATS_ADD_TX_BYTES(&tbase->aux->stats, tx_bytes); + TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes); + return tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out); +} + +int tx_pkt_drop_all(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out) +{ + for (uint16_t j = 0; j < n_pkts; ++j) { + rte_pktmbuf_free(mbufs[j]); + } + if (out == NULL) + TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, n_pkts); + else { + for (uint16_t j = 0; j < n_pkts; ++j) { + if (out[j] == OUT_HANDLED) + TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, 1); + else + TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, 1); + } + } + return n_pkts; +} diff --git a/VNFs/DPPD-PROX/tx_pkt.h b/VNFs/DPPD-PROX/tx_pkt.h new file mode 100644 index 00000000..798797ab --- /dev/null +++ b/VNFs/DPPD-PROX/tx_pkt.h @@ -0,0 +1,82 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _TX_PKT_H_ +#define _TX_PKT_H_ + +#include <inttypes.h> + +struct task_base; +struct rte_mbuf; + +void flush_queues_hw(struct task_base *tbase); +void flush_queues_sw(struct task_base *tbase); + +void flush_queues_no_drop_hw(struct task_base *tbase); +void flush_queues_no_drop_sw(struct task_base *tbase); + +/* The following four transmit functions always send packets to the + single output unless the packet should be dropped. These functions + are used if (1) the task is only sending to one destination and + (2), packets can potentially be dropped (as specified by the out + parameter, which is either NO_PORT_AVAIL or 0). */ +int tx_pkt_no_drop_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_no_drop_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + +/* The following four transmit functions are used if (1) the task is + only sending to one destination and (2), packets are never dropped + by the task (the out parameter is ignored). */ +int tx_pkt_no_drop_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_no_drop_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_no_drop_never_discard_hw1_no_pointer(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_no_drop_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + +/* The two "self" transmit functions are used if the task is + transmitting to another task running on the same core and the + destination task ID is one higher than the current task. The never_discard + version of the function ignores the out parameter and should + therefor only be used if the task never discards packets.*/ +int tx_pkt_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); +int tx_pkt_never_discard_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out); + +/* The following four tarnsmit functions are the most general. They + are used if (1) packets can be dropped and (2) there are multiple + outputs in the task. */ +int tx_pkt_no_drop_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_no_drop_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); + +int tx_pkt_trace(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_dump(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_distr(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); +int tx_pkt_bw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); + +uint16_t tx_try_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +uint16_t tx_try_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); +uint16_t tx_try_self(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts); + +/* When there are no output ports, this function is configured as the + tx function. This tx function can be used to make each task a + sink. */ +int tx_pkt_drop_all(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out); + +#endif /* _TX_PKT_H_ */ diff --git a/VNFs/DPPD-PROX/version.h b/VNFs/DPPD-PROX/version.h new file mode 100644 index 00000000..b906b14b --- /dev/null +++ b/VNFs/DPPD-PROX/version.h @@ -0,0 +1,34 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define STRINGIFY(s) #s +#define SSTR(s) STRINGIFY(s) + +/* PROGRAM_NAME defined through Makefile */ +#define VERSION_MAJOR 0 +#define VERSION_MINOR 39 +#define VERSION_REV 0 + +#if VERSION_REV > 0 +#define VERSION_STR "v" SSTR(VERSION_MAJOR) "." SSTR(VERSION_MINOR) "." SSTR(VERSION_REV) +#else +#define VERSION_STR "v" SSTR(VERSION_MAJOR) "." SSTR(VERSION_MINOR) +#endif + +#endif /* _VERSION_H_ */ diff --git a/VNFs/DPPD-PROX/vxlangpe_nsh.h b/VNFs/DPPD-PROX/vxlangpe_nsh.h new file mode 100644 index 00000000..2e7cfc76 --- /dev/null +++ b/VNFs/DPPD-PROX/vxlangpe_nsh.h @@ -0,0 +1,44 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef _VXLANGPE_NSH_H_ +#define _VXLANGPE_NSH_H_ + +struct nsh_hdr { + uint16_t version :2; + uint16_t oa_flag :1; + uint16_t cm_flag :1; + uint16_t reserved :6; + uint16_t length :6; + uint8_t md_type; + uint8_t next_proto; + uint32_t sfp_index :24; + uint32_t sf_index :8; + uint32_t ctx_1; + uint32_t ctx_2; + uint32_t ctx_3; + uint32_t ctx_4; +} __attribute__((__packed__)); + +struct vxlan_gpe_hdr { + uint8_t flag_0; + uint8_t flag_1; + uint8_t reserved; + uint8_t next_proto; + uint32_t vni_res; +} __attribute__((__packed__)); + +#endif /* _VXLANGPE_NSH_H_ */ |