aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt173
-rw-r--r--README.md189
-rw-r--r--cmake/FindVPP.cmake209
-rw-r--r--cmake/Packager.cmake151
-rw-r--r--src/dpi.api42
-rw-r--r--src/dpi.api.h179
-rw-r--r--src/dpi.c723
-rw-r--r--src/dpi.h330
-rw-r--r--src/dpi_api.c160
-rw-r--r--src/dpi_app_match.h124
-rw-r--r--src/dpi_cli.c357
-rw-r--r--src/dpi_node.c1035
-rw-r--r--src/dpi_plugin_doc.md107
-rw-r--r--src/protocols/dpi_ssl.c247
14 files changed, 4016 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..95d5cad
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,173 @@
+# Copyright (c) 2017-2019 Cisco, Intel and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
+project(udpi-plugin)
+
+include(GNUInstallDirs)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
+ "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/"
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/"
+)
+
+set (CMAKE_CXX_STANDARD 11)
+set (CMAKE_C_STANDARD 11)
+
+# Check for memfd_create syscall
+include(CheckSymbolExists)
+CHECK_SYMBOL_EXISTS ( "__NR_memfd_create" "sys/syscall.h" HAVE_MEMFD_CREATE )
+if ( HAVE_MEMFD_CREATE )
+ add_definitions ( -DHAVE_MEMFD_CREATE )
+endif()
+
+if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+ set(UDPI_PLUGIN udpi-plugin)
+endif()
+
+include (Packager)
+
+# Dependencies
+
+message(STATUS "Looking for Hyperscan")
+find_path(HYPERSCAN_INCLUDE_DIR NAMES hs/hs.h)
+find_library(HYPERSCAN_LIB1 NAMES hs)
+find_library(HYPERSCAN_LIB2 NAMES hs_runtime)
+set (HYPERSCAN_LIB ${HYPERSCAN_LIB1} ${HYPERSCAN_LIB2})
+if(HYPERSCAN_INCLUDE_DIR AND HYPERSCAN_LIB)
+ include_directories(${HYPERSCAN_INCLUDE_DIR})
+ message(STATUS "Found Hyperscan in ${HYPERSCAN_INCLUDE_DIR}")
+else()
+ message(WARNING "-- Hyperscan not found - dpi_plugin disabled")
+endif()
+
+find_package(VPP REQUIRED)
+
+include_directories(${VPP_INCLUDE_DIR})
+
+set(UDPI_PLUGIN_FILES
+ src/dpi.c
+ src/dpi_api.c
+ src/dpi_cli.c
+ src/dpi_node.c
+ src/protocols/dpi_ssl.c
+)
+
+set(UDPI_PLUGIN_HEADER_FILES
+ src/dpi_app_match.h
+ src/dpi.h
+ )
+
+set(UDPI_API_TEST_SOURCE_FILES
+ )
+
+set(UDPI_API_GENERATED_FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.h)
+
+set(UDPI_VAPI_GENERATED_FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.vapi.h
+ ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.vapi.hpp)
+
+if (NOT VPP_HOME)
+ set(VPP_HOME /usr)
+endif()
+
+if (NOT CMAKE_BUILD_TYPE)
+ set (CMAKE_BUILD_TYPE "Release")
+endif (NOT CMAKE_BUILD_TYPE)
+
+SET(UDPI_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} CACHE STRING "udpi_install_prefix")
+
+if (CMAKE_BUILD_TYPE STREQUAL "Release")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wall -march=native -O3 -g")
+elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -march=native -O0 -g")
+ add_definitions(-DCLIB_DEBUG -fPIC -fstack-protector-all)
+endif()
+
+execute_process(
+ COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi
+ )
+# These files are missing from vpp binary distribution
+execute_process(
+ COMMAND curl https://git.fd.io/vpp/plain/src/vpp-api/vapi/vapi_json_parser.py?h=stable/1908 -o ${CMAKE_BINARY_DIR}/vapi_json_parser.py
+ COMMAND curl https://git.fd.io/vpp/plain/src/vpp-api/vapi/vapi_c_gen.py?h=stable/1908 -o ${CMAKE_BINARY_DIR}/vapi_c_gen.py
+ COMMAND curl https://git.fd.io/vpp/plain/src/vpp-api/vapi/vapi_cpp_gen.py?h=stable/1908 -o ${CMAKE_BINARY_DIR}/vapi_cpp_gen.py
+ )
+
+add_custom_command(
+ COMMAND chmod +x ${CMAKE_BINARY_DIR}/vapi_json_parser.py ${CMAKE_BINARY_DIR}/vapi_c_gen.py ${CMAKE_BINARY_DIR}/vapi_cpp_gen.py
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.h
+ COMMAND ${VPP_HOME}/bin/vppapigen --input ${CMAKE_CURRENT_SOURCE_DIR}/src/dpi.api --output ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.h
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/dpi.api
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.json
+ COMMAND ${VPP_HOME}/bin/vppapigen JSON --input ${CMAKE_CURRENT_SOURCE_DIR}/src/dpi.api --output ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.json
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.vapi.h
+ COMMAND ${CMAKE_BINARY_DIR}/vapi_c_gen.py ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.json
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.vapi.hpp
+ COMMAND ${CMAKE_BINARY_DIR}/vapi_cpp_gen.py ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi/dpi.api.json
+ )
+
+include_directories(SYSTEM)
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUDPI_VPP_PLUGIN=1")
+add_library(udpi_plugin SHARED
+ ${UDPI_PLUGIN_FILES}
+ ${UDPI_API_GENERATED_FILES}
+ ${UDPI_VAPI_GENERATED_FILES})
+target_link_libraries(udpi_plugin ${HYPERSCAN_LIB})
+
+file(COPY ${UDPI_PLUGIN_HEADER_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins/udpi)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/vpp_plugins)
+
+add_library(udpi_api_test_plugin SHARED
+ ${UDPI_API_TEST_SOURCE_FILES}
+ ${UDPI_API_GENERATED_FILES})
+
+set(VPP_INSTALL_PLUGIN ${UDPI_INSTALL_PREFIX}/vpp_plugins)
+set(VPP_INSTALL_API_TEST_PLUGIN ${UDPI_INSTALL_PREFIX}/vpp_api_test_plugins CACHE STRING "vpp_install_api_test_plugin")
+
+set_target_properties(udpi_plugin
+ PROPERTIES
+ LINKER_LANGUAGE C
+ INSTALL_RPATH ${VPP_INSTALL_PLUGIN}
+ PREFIX "")
+set_target_properties(udpi_api_test_plugin
+ PROPERTIES
+ LINKER_LANGUAGE C
+ PREFIX "")
+
+install(DIRECTORY
+ DESTINATION ${VPP_INSTALL_PLUGIN}
+ COMPONENT ${UDPI_PLUGIN})
+install(TARGETS udpi_plugin
+ DESTINATION ${VPP_INSTALL_PLUGIN}
+ COMPONENT ${UDPI_PLUGIN})
+
+#install(DIRECTORY
+# DESTINATION ${VPP_INSTALL_API_TEST_PLUGIN}
+# COMPONENT ${UDPI_PLUGIN})
+#install(TARGETS udpi_api_test_plugin
+# DESTINATION ${VPP_INSTALL_API_TEST_PLUGIN}
+# COMPONENT ${UDPI_PLUGIN})
+
+install(FILES ${UDPI_API_HEADER_FILES} ${UDPI_API_GENERATED_FILES}
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/vpp_plugins/udpi
+ COMPONENT ${UDPI_PLUGIN}-dev)
+
+install(FILES ${UDPI_VAPI_GENERATED_FILES}
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/vapi
+ COMPONENT ${UDPI_PLUGIN}-dev)
+
+make_packages()
diff --git a/README.md b/README.md
index bb6f9b0..eb41715 100644
--- a/README.md
+++ b/README.md
@@ -6,23 +6,192 @@ UDPI
The UDPI (Universal Deep Packet Inspection) project is a reference framework to build a high performance solution
for Deep Packet Inspection, integrated with the general purpose FD.io VPP stack.
It leverages industry regex matching library to provide a rich set of features,
-which can be used in IPS/IDS, Web Firewall and similar applications.
+ which can be used in IPS/IDS, Web Firewall and similar applications.
-For more information on UDPI and its features please visit the
-[UDPI website](https://wiki.fd.io/view/UDPI)
+ For more information on UDPI and its features please visit the
+ [UDPI website](https://wiki.fd.io/view/UDPI)
## Changes
-Details of the changes leading up to this version of UDPI can be found under
-@ref release notes.
+ Details of the changes leading up to this version of UDPI can be found under
+ @ref release notes.
+## Quick Start ##
+```
+From the code tree root
-## Getting started
+(VPP installed with DEB or RPM pkg)
+$ cd udpi-plugin
+$ mkdir -p build
+$ cd build
+$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr #(add "-DCMKAE_INSTALL_LIBDIR=lib" on centos and it's releated)
+$ make package
-Make sure you have added FD.io repository using https://packagecloud.io/fdio/release/
-installation script.
-You should have a sight on the release package, the package name may be different depending on the distribution.
-(ex: vpp-plugins.deb for VPP 19.01 and vpp-plugin-core.deb and vpp-plugin-dpdk.deb in 19.04)
+(VPP source code -- build type RELEASE)
+$ cd udpi-plugin
+$ mkdir -p build
+$ cd build
+$ cmake .. -DVPP_HOME=<vpp dir>/build-root/install-vpp-native/vpp -DCMAKE_INSTALL_PREFIX=<vpp src>/build-root/install-vpp-native/vpp #(add "-DCMKAE_INSTALL_LIBDIR=lib" on centos and it's releated)
+$ make
+$ sudo make install
+(VPP source code -- build type DEBUG)
+$ cd udpi-plugin
+$ mkdir -p build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG -DVPP_HOME=<vpp dir>/build-root/install-vpp_debug-native/vpp -DCMAKE_INSTALL_PREFIX=<vpp src>/build-root/install-vpp_debug-native/vpp #(add "-DCMKAE_INSTALL_LIBDIR=lib" on centos and it's releated)
+$ make
+$ sudo make install
+CMAKE variables:
+- CMKAE_INSTALL_LIBDIR -- set the library directory. In CentOS, it is lib or lib64, in debian and it's releated, it is lib/x86_64-linux-gnu
+- CMAKE_INSTALL_PREFIX -- set the install directory for the udpi-plugin. This is the common path to the lib folder containing vpp_plugins and vpp_api_test_plugins folders. Default is /usr/local.
+- VPP_HOME -- set the directory containing the include and lib directories of vpp.
+```
+
+## Using udpi plugin ##
+
+### Platforms ###
+
+udpi-plugin has been tested in:
+
+- Ubuntu 16.04 LTS (x86_64)
+- Ubuntu 18.04 LTS (x86_64)
+- Debian Stable/Testing
+- Red Hat Enterprise Linux 7
+- CentOS 7
+
+
+### Dependencies ###
+
+Build dependencies:
+
+- Hyperscan
+
+- VPP 19.08
+ - DEB packages (can be found https://packagecloud.io/fdio/release/install):
+ - libvppinfra-dev
+ - vpp-dev
+
+Running dependencies:
+
+- VPP 19.08
+ - DEB packages (can be found https://packagecloud.io/fdio/release/install):
+ - vpp
+ - vpp-plugin-core
+ - vpp-plugin-dpdk (only to use DPDK compatible nics)
+
+Hardware support (not mandatory):
+
+- [DPDK](http://DPDK.org/) compatible nics
+
+## Getting started ##
+In order to start, the udpi plugin requires a running instance of VPP
+The steps required to successfully start udpi are:
+
+- Setup the host to run VPP
+- Configure VPP to use DPDK compatible nics
+- Start VPP
+- Configure VPP interfaces
+- Configure and start udpi
+
+Detailed information for configuring VPP can be found at [https://wiki.fd.io/view/VPP](https://wiki.fd.io/view/VPP).
+
+### Setup the host for VPP ###
+
+Hugepages must be enabled in the system
+
+```
+$ sudo sysctl -w vm.nr_hugepages=1024
+```
+
+In order to use a DPDK interface, the `uio` and `uio_pci_generic` or `vfio_pci` modules need to be loaded in the kernel
+
+```
+$ sudo modprobe uio
+$ sudo modprobe uio_pci_generic
+$ sudo modprobe vfio_pci
+```
+
+If the DPDK interface we want to assign to VPP is up, we must bring it down
+
+```
+$ sudo ifconfig <interface_name> down
+```
+or
+```
+$ sudo ip link set <interface_name> down
+```
+
+### Configure VPP ###
+The file /etc/VPP/startup.conf contains a set of parameters to setup VPP at startup.
+The following example sets up VPP to use a DPDK interfaces:
+
+``` shell
+unix {
+ nodaemon
+ log /tmp/vpp.log
+ full-coredump
+}
+
+api-trace {
+ on
+}
+
+api-segment {
+ gid vpp
+}
+
+dpdk {
+ dev 0000:08:00.0
+}
+
+plugins {
+ ## Disable all plugins by default and then selectively enable specific plugins
+
+ ## Enable all plugins by default and then selectively disable specific plugins
+}
+```
+Where `0000:08:00.0` must be replaced with the actual PCI address of the DPDK interface
+
+### Start VPP ###
+
+VPP can be started as a process or a service:
+
+``` shell
+Start VPP as a service in Ubuntu 16.04
+$ sudo systemctl start vpp
+
+Start VPP as a process in both 16.04
+$ sudo vpp -c /etc/vpp/startup.conf
+
+```
+
+### Configure udpi plugin ###
+The udpi plugin can be configured either using the VPP command-line interface (CLI), through a configuration file or through the VPP binary api
+
+#### udpi plugin CLI ####
+
+The CLI commands for the udpi plugin start all with the udpi keyword. To see the full list of command available type:
+...(to be added)
+
+## License ##
+
+This software is distributed under the following license:
+
+```
+Copyright (c) 2017-2019 Cisco and/or its affiliates.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+```
diff --git a/cmake/FindVPP.cmake b/cmake/FindVPP.cmake
new file mode 100644
index 0000000..56ab482
--- /dev/null
+++ b/cmake/FindVPP.cmake
@@ -0,0 +1,209 @@
+#
+# Copyright (c) 2018 PANTHEON.tech.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# - Try to find LibVPP
+# Once done this will define
+#
+# VPP_FOUND - system has VPP
+# VPP_INCLUDE_DIRS - the VPP include directory
+# VPP_LIBRARIES - Link these to use LibSSH
+
+
+if (VPP_LIBRARIES AND VPP_INCLUDE_DIRS)
+ # in cache already
+ set(VPP_FOUND TRUE)
+else (VPP_LIBRARIES AND VPP_INCLUDE_DIRS)
+
+ set(VPP_INCLUDE_PATH
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ )
+
+ set(VPP_LIBRARY_PATH
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ /sw/lib
+ )
+
+ find_path(VNET_INCLUDE_DIR
+ NAMES
+ vnet/vnet.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+
+ )
+
+ find_path(VLIB_API_INCLUDE_DIR
+ NAMES
+ vlibapi/api.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_path(VLIBMEMORY_INCLUDE_DIR
+ NAMES
+ vlibmemory/api.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_path(VPP_MSG_INCLUDE_DIR
+ NAMES
+ vpp/api/vpe_msg_enum.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_path(VPP_ALL_INCLUDE_DIR
+ NAMES
+ vpp/api/vpe_all_api_h.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_path(VAPI_INCLUDE_DIR
+ NAMES
+ vapi/interface.api.vapi.h
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_path(VOM_INCLUDE_DIR
+ NAMES
+ vom/om.hpp
+ PATHS
+ ${VPP_INCLUDE_PATH}
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(VLIBMEMORYCLIENT_LIBRARY
+ NAMES
+ vlibmemoryclient
+ libvlibmemoryclient
+ PATHS
+ ${VPP_LIBARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(SVM_LIBRARY
+ NAMES
+ svm
+ libsvm
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(VPPINFRA_LIBRARY
+ NAMES
+ vppinfra
+ libvppinfra
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(VLIB_LIBRARY
+ NAMES
+ vlib
+ libvlib
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(VATPLUGIN_LIBRARY
+ NAMES
+ vatplugin
+ libvatplugin
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(VAPI_LIBRARY
+ NAMES
+ vapiclient
+ libvapiclient
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ find_library(VOM_LIBRARY
+ NAMES
+ vom
+ libvom
+ PATHS
+ ${VPP_LIBRARY_PATH}
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if (VPP_INCLUDE_DIR AND VPP_LIBRARY)
+ set(VPP_FOUND TRUE)
+ else (VPP_INCLUDE_DIR AND VPP_LIBRARY)
+ set(VPP_FOUND FALSE)
+ endif (VPP_INCLUDE_DIR AND VPP_LIBRARY)
+
+ set(VPP_INCLUDE_DIRS
+ ${VNET_INCLUDE_DIR}
+ ${VLIB_API_INCLUDE_DIR}
+ ${VLIB_MEMORY_INCLUDE_DIR}
+ ${VPP_MSG_INCLUDE_DIR}
+ ${VPP_ALL_INCLUDE_DIR}
+ ${VAPI_INCLUDE_DIR}
+ ${VOM_INCLUDE_DIR}
+ )
+
+ set(VPP_LIBRARIES
+ ${VLIBMEMORYCLIENT_LIBRARY}
+ ${SVM_LIBRARY}
+ ${VPPINFRA_LIBRARY}
+ ${VLIB_LIBRARY}
+ ${VATPLUGIN_LIBRARY}
+ ${VAPI_LIBRARY}
+ ${VOM_LIBRARY}
+ )
+
+ # show the VPP_INCLUDE_DIRS and VPP_LIBRARIES variables only in the advanced view
+ mark_as_advanced(VPP_INCLUDE_DIRS VPP_LIBRARIES)
+
+endif (VPP_LIBRARIES AND VPP_INCLUDE_DIRS)
diff --git a/cmake/Packager.cmake b/cmake/Packager.cmake
new file mode 100644
index 0000000..b049819
--- /dev/null
+++ b/cmake/Packager.cmake
@@ -0,0 +1,151 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#############
+# RPM/DEB/TGZ Packaging utils
+#
+
+set(CONTACT "udpi-dev@lists.fd.io" CACHE STRING "Contact")
+set(PACKAGE_MAINTAINER "Udpi Team" CACHE STRING "Maintainer")
+set(PACKAGE_VENDOR "fd.io" CACHE STRING "Vendor")
+
+# macro(set)
+
+macro(make_packages)
+ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+ # parse /etc/os-release
+ file(READ "/etc/os-release" os_version)
+ string(REPLACE "\n" ";" os_version ${os_version})
+ foreach(_ver ${os_version})
+ string(REPLACE "=" ";" _ver ${_ver})
+ list(GET _ver 0 _name)
+ list(GET _ver 1 _value)
+ set(OS_${_name} ${_value})
+ endforeach()
+
+ #extract version from git
+ execute_process(
+ COMMAND git describe --long --tags
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ OUTPUT_VARIABLE VER
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ if (NOT VER)
+ set(VER "v1.0")
+ endif()
+
+ string(REGEX REPLACE "v(.*)-([0-9]+)-(g[0-9a-f]+)" "\\1;\\2;\\3" VER ${VER})
+ list(GET VER 0 tag)
+ string(REPLACE "-" "~" tag ${tag})
+ list(GET VER 1 commit_num)
+ list(GET VER 2 commit_name)
+
+ message("${tag}")
+
+ if (NOT DEFINED ENV{BUILD_NUMBER})
+ set(bld "b1")
+ else()
+ set(bld "b$ENV{BUILD_NUMBER}")
+ endif()
+
+ message("Build number is: ${bld}")
+
+ #define DEB and RPM version numbers
+ if(${commit_num} EQUAL 0)
+ set(deb_ver "${tag}")
+ set(rpm_ver "${tag}")
+ else()
+ set(deb_ver "${tag}-${commit_num}-${commit_name}")
+ set(rpm_ver "${tag}-${commit_num}-${commit_name}")
+ endif()
+
+ get_cmake_property(components COMPONENTS)
+
+ if(OS_ID_LIKE MATCHES "debian")
+ set(CPACK_GENERATOR "DEB")
+ set(type "DEBIAN")
+
+ execute_process(
+ COMMAND dpkg --print-architecture
+ OUTPUT_VARIABLE arch
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ set(CPACK_PACKAGE_VERSION "${deb_ver}")
+ foreach(lc ${components})
+ if (${lc} MATCHES ".*Unspecified.*")
+ continue()
+ endif()
+
+ string(TOUPPER ${lc} uc)
+ set(CPACK_${type}_${uc}_FILE_NAME "${lc}_${deb_ver}_${arch}.deb")
+
+ set(DEB_DEPS)
+ if (NOT ${${lc}_DEB_DEPENDENCIES} STREQUAL "")
+ string(REPLACE "stable_version" ${tag} DEB_DEPS ${${lc}_DEB_DEPENDENCIES})
+ endif()
+
+ set(CPACK_${type}_${uc}_PACKAGE_DEPENDS "${DEB_DEPS}")
+ set(CPACK_${type}_${uc}_PACKAGE_NAME "${lc}")
+ set(CPACK_COMPONENT_${uc}_DESCRIPTION "${${lc}_DESCRIPTION}")
+ endforeach()
+ elseif(OS_ID_LIKE MATCHES "rhel")
+ set(CPACK_GENERATOR "RPM")
+ set(type "RPM")
+
+ execute_process(
+ COMMAND uname -m
+ OUTPUT_VARIABLE arch
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ set(CPACK_PACKAGE_VERSION "${rpm_ver}")
+ foreach(lc ${components})
+ if (${lc} MATCHES ".*Unspecified.*")
+ continue()
+ endif()
+
+ string(TOUPPER ${lc} uc)
+ set(CPACK_${type}_${uc}_DESCRIPTION "${${lc}_DESCRIPTION}")
+
+ set(RPM_DEPS)
+ if (NOT ${${lc}_DEB_DEPENDENCIES} STREQUAL "")
+ string(REPLACE "stable_version" ${tag} RPM_DEPS ${${lc}_RPM_DEPENDENCIES})
+ endif()
+
+ set(CPACK_${type}_${uc}_PACKAGE_REQUIRES "${RPM_DEPS}")
+
+ if(${lc} MATCHES ".*-dev")
+ set(package_name ${lc}el)
+ else()
+ set(package_name ${lc})
+ endif()
+
+ set(CPACK_RPM_${uc}_PACKAGE_NAME "${package_name}")
+ set(CPACK_${type}_${uc}_FILE_NAME "${package_name}-${rpm_ver}.${arch}.rpm")
+ endforeach()
+ endif()
+
+ if(CPACK_GENERATOR)
+ set(CPACK_PACKAGE_NAME ${ARG_NAME})
+ set(CPACK_STRIP_FILES OFF)
+ set(CPACK_PACKAGE_VENDOR "${PACKAGE_VENDOR}")
+ set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
+ set(CPACK_${CPACK_GENERATOR}_COMPONENT_INSTALL ON)
+ set(CPACK_${type}_PACKAGE_MAINTAINER "Udpi Team")
+ set(CPACK_${type}_PACKAGE_RELEASE 1)
+ include(CPack)
+ endif()
+ endif()
+endmacro()
diff --git a/src/dpi.api b/src/dpi.api
new file mode 100644
index 0000000..b9fa99e
--- /dev/null
+++ b/src/dpi.api
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+option version = "1.0.0";
+
+define dpi_flow_add_del {
+ u32 client_index;
+ u32 context;
+ u8 is_add;
+ u8 is_ipv6;
+ u8 src_ip[16];
+ u8 dst_ip[16];
+ u16 src_port;
+ u16 dst_port;
+ u32 vrf_id;
+ u8 protocol;
+};
+
+define dpi_flow_add_del_reply
+{
+ u32 context;
+ i32 retval;
+ u32 flow_id;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi.api.h b/src/dpi.api.h
new file mode 100644
index 0000000..3ee50db
--- /dev/null
+++ b/src/dpi.api.h
@@ -0,0 +1,179 @@
+/*
+ * VLIB API definitions 2019-09-02 14:52:52
+ * Input file: dpi.api
+ * Automatically generated: please edit the input file NOT this file!
+ */
+
+#include <stdbool.h>
+#if defined(vl_msg_id)||defined(vl_union_id) \
+ || defined(vl_printfun) ||defined(vl_endianfun) \
+ || defined(vl_api_version)||defined(vl_typedefs) \
+ || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \
+ || defined(vl_api_version_tuple)
+/* ok, something was selected */
+#else
+#warning no content included from dpi.api
+#endif
+
+#define VL_API_PACKED(x) x __attribute__ ((packed))
+
+/****** Message ID / handler enum ******/
+
+#ifdef vl_msg_id
+vl_msg_id(VL_API_DPI_FLOW_ADD_DEL, vl_api_dpi_flow_add_del_t_handler)
+vl_msg_id(VL_API_DPI_FLOW_ADD_DEL_REPLY, vl_api_dpi_flow_add_del_reply_t_handler)
+#endif
+/****** Message names ******/
+
+#ifdef vl_msg_name
+vl_msg_name(vl_api_dpi_flow_add_del_t, 1)
+vl_msg_name(vl_api_dpi_flow_add_del_reply_t, 1)
+#endif
+/****** Message name, crc list ******/
+
+#ifdef vl_msg_name_crc_list
+#define foreach_vl_msg_name_crc_dpi \
+_(VL_API_DPI_FLOW_ADD_DEL, dpi_flow_add_del, 52a12407) \
+_(VL_API_DPI_FLOW_ADD_DEL_REPLY, dpi_flow_add_del_reply, e467dfee)
+#endif
+
+/****** Typedefs ******/
+
+#ifdef vl_typedefs
+#ifndef included_dpi_api
+#define included_dpi_api
+#ifndef _vl_api_defined_dpi_flow_add_del
+#define _vl_api_defined_dpi_flow_add_del
+typedef VL_API_PACKED(struct _vl_api_dpi_flow_add_del {
+ u16 _vl_msg_id;
+ u32 client_index;
+ u32 context;
+ u8 is_add;
+ u8 is_ipv6;
+ u8 src_ip[16];
+ u8 dst_ip[16];
+ u16 src_port;
+ u16 dst_port;
+ u32 vrf_id;
+ u8 protocol;
+}) vl_api_dpi_flow_add_del_t;
+#endif
+
+#ifndef _vl_api_defined_dpi_flow_add_del_reply
+#define _vl_api_defined_dpi_flow_add_del_reply
+typedef VL_API_PACKED(struct _vl_api_dpi_flow_add_del_reply {
+ u16 _vl_msg_id;
+ u32 context;
+ i32 retval;
+ u32 flow_id;
+}) vl_api_dpi_flow_add_del_reply_t;
+#endif
+
+
+#endif
+#endif
+
+/****** Print functions *****/
+#ifdef vl_printfun
+
+#ifdef LP64
+#define _uword_fmt "%lld"
+#define _uword_cast (long long)
+#else
+#define _uword_fmt "%ld"
+#define _uword_cast long
+#endif
+
+#ifndef _vl_api_defined_dpi_flow_add_del_t_print
+#define _vl_api_defined_dpi_flow_add_del_t_print
+static inline void *vl_api_dpi_flow_add_del_t_print (vl_api_dpi_flow_add_del_t *a,void *handle)
+{
+ vl_print(handle, "vl_api_dpi_flow_add_del_t:\n");
+ vl_print(handle, "_vl_msg_id: %u\n", a->_vl_msg_id);
+ vl_print(handle, "client_index: %u\n", a->client_index);
+ vl_print(handle, "context: %u\n", a->context);
+ vl_print(handle, "is_add: %u\n", a->is_add);
+ vl_print(handle, "is_ipv6: %u\n", a->is_ipv6);
+ vl_print(handle, "src_port: %u\n", a->src_port);
+ vl_print(handle, "dst_port: %u\n", a->dst_port);
+ vl_print(handle, "vrf_id: %u\n", a->vrf_id);
+ vl_print(handle, "protocol: %u\n", a->protocol);
+ return handle;
+}
+
+#endif
+
+#ifndef _vl_api_defined_dpi_flow_add_del_reply_t_print
+#define _vl_api_defined_dpi_flow_add_del_reply_t_print
+static inline void *vl_api_dpi_flow_add_del_reply_t_print (vl_api_dpi_flow_add_del_reply_t *a,void *handle)
+{
+ vl_print(handle, "vl_api_dpi_flow_add_del_reply_t:\n");
+ vl_print(handle, "_vl_msg_id: %u\n", a->_vl_msg_id);
+ vl_print(handle, "context: %u\n", a->context);
+ vl_print(handle, "retval: %ld\n", a->retval);
+ vl_print(handle, "flow_id: %u\n", a->flow_id);
+ return handle;
+}
+
+#endif
+
+
+#endif /* vl_printfun */
+
+/****** Endian swap functions *****/
+#ifdef vl_endianfun
+
+#undef clib_net_to_host_uword
+#ifdef LP64
+#define clib_net_to_host_uword clib_net_to_host_u64
+#else
+#define clib_net_to_host_uword clib_net_to_host_u32
+#endif
+
+#ifndef _vl_api_defined_dpi_flow_add_del_t_endian
+#define _vl_api_defined_dpi_flow_add_del_t_endian
+static inline void vl_api_dpi_flow_add_del_t_endian (vl_api_dpi_flow_add_del_t *a)
+{
+ a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id);
+ a->client_index = clib_net_to_host_u32(a->client_index);
+ a->context = clib_net_to_host_u32(a->context);
+ /* a->is_add = a->is_add (no-op) */
+ /* a->is_ipv6 = a->is_ipv6 (no-op) */
+ a->src_port = clib_net_to_host_u16(a->src_port);
+ a->dst_port = clib_net_to_host_u16(a->dst_port);
+ a->vrf_id = clib_net_to_host_u32(a->vrf_id);
+ /* a->protocol = a->protocol (no-op) */
+}
+
+#endif
+
+#ifndef _vl_api_defined_dpi_flow_add_del_reply_t_endian
+#define _vl_api_defined_dpi_flow_add_del_reply_t_endian
+static inline void vl_api_dpi_flow_add_del_reply_t_endian (vl_api_dpi_flow_add_del_reply_t *a)
+{
+ a->_vl_msg_id = clib_net_to_host_u16(a->_vl_msg_id);
+ a->context = clib_net_to_host_u32(a->context);
+ a->retval = clib_net_to_host_u32(a->retval);
+ a->flow_id = clib_net_to_host_u32(a->flow_id);
+}
+
+#endif
+
+
+#endif /* vl_endianfun */
+
+/****** Version tuple *****/
+
+#ifdef vl_api_version_tuple
+
+vl_api_version_tuple(dpi.api, 1, 0, 0)
+
+#endif /* vl_api_version_tuple */
+
+/****** API CRC (whole file) *****/
+
+#ifdef vl_api_version
+vl_api_version(dpi.api, 0x79814222)
+
+#endif
+
diff --git a/src/dpi.c b/src/dpi.c
new file mode 100644
index 0000000..6423f3a
--- /dev/null
+++ b/src/dpi.c
@@ -0,0 +1,723 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/mfib/mfib_table.h>
+//#include <vnet/adj/adj_mcast.h>
+#include <vnet/dpo/dpo.h>
+#include <vnet/plugin/plugin.h>
+//#include <vpp/app/version.h>
+#include <vnet/flow/flow.h>
+
+#include "dpi.h"
+
+dpi_main_t dpi_main;
+dpi_entry_t *dpi_dbs = NULL;
+
+#if CLIB_DEBUG > 0
+#define dpi_debug clib_warning
+#else
+#define dpi_debug(...) \
+ do { } while (0)
+#endif
+
+/* Here rules are extracted from below link with BSD License
+ * https://rules.emergingthreats.net/open-nogpl/snort-2.9.0/emerging-all.rules */
+
+dpi_app_match_rule app_match_rules[] = {
+ {"www.cisco.com", NULL, "Cisco", DPI_APP_CISCO}
+ ,
+ {"*.google.com", NULL, "Google", DPI_APP_GOOGLE}
+ ,
+ {"www.bing.com", NULL, "Bing", DPI_APP_BING}
+ ,
+ {"www.msn.com", NULL, "MSN", DPI_APP_MSN}
+ ,
+ {"www.yahoo.com", NULL, "", DPI_APP_YAHOO}
+ ,
+ {"mail.yahoo.com", NULL, "YahooMail", DPI_APP_YAHOOMAIL}
+ ,
+ {"www.intel.com", NULL, "Intel", DPI_APP_INTEL}
+ ,
+ {"*.amazon.com", NULL, "Amazon", DPI_APP_AMAZON}
+ ,
+ {"*.amd.com", NULL, "AMD", DPI_APP_AMD}
+ ,
+ {"*.baidu.com", NULL, "Baidu", DPI_APP_BAIDU}
+ ,
+ {"*.apple.com", NULL, "Apple", DPI_APP_APPLE}
+ ,
+ {"*.facebook.com", NULL, "Facebook", DPI_APP_FACEBOOK}
+ ,
+ {"*.ebay.com", NULL, "Ebay", DPI_APP_EBAY}
+ ,
+ {"*.github.com", NULL, "GitHub", DPI_APP_GITHUB}
+ ,
+ {"*.gmail.com", NULL, "Gmail", DPI_APP_GMAIL}
+ ,
+ {"*.qq.com", NULL, "QQ", DPI_APP_QQ}
+ ,
+ {"weixin.qq.com", NULL, "Wechat", DPI_APP_WECHAT}
+ ,
+ {"*.pinterest.com", NULL, "", DPI_APP_PINTEREST}
+ ,
+ {"*.lenovo.com", NULL, "Levono", DPI_APP_LENOVO}
+ ,
+ {"*.linkedin.com", NULL, "LinkedIn", DPI_APP_LINKEDIN}
+ ,
+ {"*.skype.com", NULL, "Skype", DPI_APP_SKYPE}
+ ,
+ {"*.microsoft.com", NULL, "Microsoft", DPI_APP_MICROSOFT}
+ ,
+ {"*.netflix.com", NULL, "Netflix", DPI_APP_NETFLIX}
+ ,
+ {"*.nokia.com", NULL, "Nokia", DPI_APP_NOKIA}
+ ,
+ {"*.nvidia.com", NULL, "nVIDIA", DPI_APP_NVIDIA}
+ ,
+ {"*.office365.com", NULL, "Office", DPI_APP_OFFICE}
+ ,
+ {"*.oracle.com", NULL, "Oracle", DPI_APP_ORACLE}
+ ,
+ {"*.Outlook.com", NULL, "Outlook", DPI_APP_OUTLOOK}
+ ,
+ {"*.pandora.com", NULL, "Pandora", DPI_APP_PANDORA}
+ ,
+ {"*.paypal.com", NULL, "Paypal", DPI_APP_PAYPAL}
+ ,
+ {"*.sina.com", NULL, "Sina", DPI_APP_SINA}
+ ,
+ {"*.sogou.com", NULL, "Sogou", DPI_APP_SOGOU}
+ ,
+ {"*.symantec.com", NULL, "Symantec", DPI_APP_SYMANTEC}
+ ,
+ {"*.taobao.com", NULL, "Taobao", DPI_APP_TAOBAO}
+ ,
+ {"*.twitter.com", NULL, "Twitter", DPI_APP_TWITTER}
+ ,
+ {"*.ups.com", NULL, "UPS", DPI_APP_UPS}
+ ,
+ {"*.visa.com", NULL, "VISA", DPI_APP_VISA}
+ ,
+ {"*.mcafee.com", NULL, "Mcafee", DPI_APP_MCAFEE}
+ ,
+ {"*.vmware.com", NULL, "VMWare", DPI_APP_VMWARE}
+ ,
+ {"*.wordpress.com", NULL, "Wordpress", DPI_APP_WORDPRESS}
+ ,
+ {"www.adobe.com", NULL, "Adobe", DPI_APP_ADOBE}
+ ,
+ {"www.akamai.com", NULL, "Akamai", DPI_APP_AKAMAI}
+ ,
+ {"*.alienvault.com", NULL, "Alienvault", DPI_APP_ALIENVAULT}
+ ,
+ {"www.bitcomet.com", NULL, "Bitcomet", DPI_APP_BITCOMET}
+ ,
+ {"www.checkpoint.com", NULL, "Checkpoint", DPI_APP_CHECKPOINT}
+ ,
+ {"*.bloomberg.com", NULL, "Bloomberg", DPI_APP_BLOOMBERG}
+ ,
+ {"www.dell.com", NULL, "DELL", DPI_APP_DELL}
+ ,
+ {"www.f5.com", NULL, "F5", DPI_APP_F5}
+ ,
+ {"www.fireeye.com", NULL, "Fireeye", DPI_APP_FIREEYE}
+ ,
+ {"*.dropbox.com", NULL, "", DPI_APP_DROPBOX}
+ ,
+
+ {NULL, NULL, NULL, 0}
+};
+
+int
+dpi_event_handler (unsigned int id, unsigned long long from,
+ unsigned long long to, unsigned int flags, void *ctx)
+{
+ (void) from;
+ (void) to;
+ (void) flags;
+
+ dpi_cb_args_t *args = (dpi_cb_args_t *) ctx;
+
+ args->res = 1;
+ args->id = id;
+
+ return 0;
+}
+
+int
+dpi_search_host_protocol (dpi_flow_info_t * flow,
+ char *str_to_match,
+ u32 str_to_match_len,
+ u16 master_protocol_id, u32 * host_protocol_id)
+{
+ dpi_main_t *dm = &dpi_main;
+ dpi_entry_t entry = dm->default_db;
+ dpi_cb_args_t args = { };
+ int ret;
+
+ /* First search default database */
+ ret = hs_scan_stream (flow->stream,
+ (const char *) str_to_match, str_to_match_len, 0,
+ entry.scratch, dpi_event_handler, (void *) &args);
+ if ((ret != HS_SUCCESS) && (ret != HS_SCAN_TERMINATED))
+ {
+ return DPI_PROTOCOL_UNKNOWN;
+ }
+ else
+ {
+ flow->app_id = args.id;
+ flow->detect_done = 1;
+ goto done;
+ }
+
+done:
+ if (flow->app_id != ~0)
+ {
+ /* Move the protocol to right position */
+ flow->detected_protocol[1] = master_protocol_id,
+ flow->detected_protocol[0] = flow->app_id;
+ *host_protocol_id = flow->app_id;
+
+ return (flow->detected_protocol[0]);
+ }
+
+ return DPI_PROTOCOL_UNKNOWN;
+}
+
+char *
+host2hex (const char *str)
+{
+ int len, i;
+ char *hexbuf, *buf;
+
+ len = strlen (str);
+ hexbuf = (char *) malloc (len * 4 + 1);
+ if (!hexbuf)
+ return (NULL);
+
+ for (i = 0, buf = hexbuf; i < len; i++, buf += 4)
+ {
+ snprintf (buf, 5, "\\x%02x", (const char) str[i]);
+ }
+ *buf = '\0';
+
+ return hexbuf;
+}
+
+int
+dpi_create_db_entry (dpi_entry_t * entry, u32 num, u32 mode)
+{
+ hs_compile_error_t *compile_err;
+
+ if (hs_compile_multi
+ ((const char **) entry->expressions, entry->flags, entry->ids,
+ num, mode, NULL, &entry->database, &compile_err) != HS_SUCCESS)
+ {
+ return -1;
+ }
+
+ if (hs_alloc_scratch (entry->database, &entry->scratch) != HS_SUCCESS)
+ {
+ hs_free_database (entry->database);
+ entry->database = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+dpi_flow_add_del (dpi_add_del_flow_args_t * a, u32 * flow_idp)
+{
+ dpi_main_t *dm = &dpi_main;
+ vnet_main_t *vnm = dm->vnet_main;
+ dpi4_flow_key_t key4;
+ dpi6_flow_key_t key6;
+ dpi_flow_entry_t *p;
+ u32 is_ip6 = a->is_ipv6;
+ u32 flow_id;
+ dpi_flow_entry_t *flow;
+
+ int not_found;
+ if (!is_ip6)
+ {
+ key4.key[0] = a->src_ip.ip4.as_u32
+ | (((u64) a->dst_ip.ip4.as_u32) << 32);
+ key4.key[1] = (((u64) a->protocol) << 32)
+ | ((u32) clib_host_to_net_u16 (a->src_port) << 16)
+ | clib_host_to_net_u16 (a->dst_port);
+ key4.key[2] = (u64) a->fib_index;
+
+ not_found =
+ clib_bihash_search_inline_24_8 (&dm->dpi4_flow_by_key, &key4);
+ p = (void *) &key4.value;
+ }
+ else
+ {
+ key6.key[0] = a->src_ip.ip6.as_u64[0];
+ key6.key[1] = a->src_ip.ip6.as_u64[1];
+ key6.key[2] = a->dst_ip.ip6.as_u64[0];
+ key6.key[3] = a->dst_ip.ip6.as_u64[1];
+ key6.key[4] = (((u64) a->protocol) << 32)
+ | ((u32) clib_host_to_net_u16 (a->src_port) << 16)
+ | clib_host_to_net_u16 (a->dst_port);
+ key6.key[5] = (u64) a->fib_index;
+
+ not_found =
+ clib_bihash_search_inline_48_8 (&dm->dpi6_flow_by_key, &key6);
+ p = (void *) &key6.value;
+ }
+
+ if (not_found)
+ p = 0;
+
+ if (a->is_add)
+ {
+
+ /* adding a flow entry: entry must not already exist */
+ if (p)
+ return VNET_API_ERROR_TUNNEL_EXIST;
+
+ pool_get_aligned (dm->dpi_flows, flow, CLIB_CACHE_LINE_BYTES);
+ clib_memset (flow, 0, sizeof (*flow));
+ flow_id = flow - dm->dpi_flows;
+
+ /* copy from arg structure */
+#define _(x) flow->key.x = a->x;
+ foreach_copy_field;
+#undef _
+
+ flow->next_index = DPI_INPUT_NEXT_IP4_LOOKUP;
+ flow->flow_index = ~0;
+
+ pool_get_aligned (dm->dpi_infos, flow->info, CLIB_CACHE_LINE_BYTES);
+ clib_memset (flow->info, 0, sizeof (*flow->info));
+ flow->info->app_id = ~0;
+
+ int add_failed;
+ if (is_ip6)
+ {
+ key6.value = (u64) flow_id;
+ add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key,
+ &key6, 1 /*add */ );
+ }
+ else
+ {
+ key4.value = (u64) flow_id;
+ add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key,
+ &key4, 1 /*add */ );
+ }
+
+ if (add_failed)
+ {
+ pool_put (dm->dpi_infos, flow->info);
+ pool_put (dm->dpi_flows, flow);
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+
+ /* Open a Hyperscan stream for each flow */
+ hs_error_t err = hs_open_stream (dm->default_db.database, 0,
+ &(flow->info->stream));
+ if (err != HS_SUCCESS)
+ {
+ pool_put (dm->dpi_infos, flow->info);
+ pool_put (dm->dpi_flows, flow);
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+ }
+ else
+ {
+ /* deleting a flow: flow must exist */
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ flow_id = is_ip6 ? key6.value : key4.value;
+ flow_id = (u32) (flow_id & (u32) (~0));
+ flow = pool_elt_at_index (dm->dpi_flows, flow_id);
+
+ if (!is_ip6)
+ clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, &key4, 0 /*del */ );
+ else
+ clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key, &key6, 0 /*del */ );
+
+ if (flow->flow_index != ~0)
+ vnet_flow_del (vnm, flow->flow_index);
+
+ /* Close the Hyperscan stream for each flow */
+ hs_error_t err = hs_close_stream (flow->info->stream, NULL,
+ NULL, NULL);
+ if (err != HS_SUCCESS)
+ {
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+
+ pool_put (dm->dpi_infos, flow->info);
+ pool_put (dm->dpi_flows, flow);
+ }
+
+ if (flow_idp)
+ *flow_idp = flow_id;
+
+ return 0;
+}
+
+int
+dpi_reverse_flow_add_del (dpi_add_del_flow_args_t * a, u32 flow_id)
+{
+ dpi_main_t *dm = &dpi_main;
+ vnet_main_t *vnm = dm->vnet_main;
+ dpi4_flow_key_t key4;
+ dpi6_flow_key_t key6;
+ dpi_flow_entry_t *p;
+ u32 is_ip6 = a->is_ipv6;
+ dpi_flow_entry_t *flow;
+
+ int not_found;
+ if (!is_ip6)
+ {
+ key4.key[0] = a->dst_ip.ip4.as_u32
+ | (((u64) a->src_ip.ip4.as_u32) << 32);
+ key4.key[1] = (((u64) a->protocol) << 32)
+ | ((u32) clib_host_to_net_u16 (a->dst_port) << 16)
+ | clib_host_to_net_u16 (a->src_port);
+ key4.key[2] = (u64) a->fib_index;
+
+ not_found =
+ clib_bihash_search_inline_24_8 (&dm->dpi4_flow_by_key, &key4);
+ p = (void *) &key4.value;
+ }
+ else
+ {
+ key6.key[0] = a->dst_ip.ip6.as_u64[0];
+ key6.key[1] = a->dst_ip.ip6.as_u64[1];
+ key6.key[2] = a->dst_ip.ip6.as_u64[0];
+ key6.key[3] = a->dst_ip.ip6.as_u64[1];
+ key6.key[4] = (((u64) a->protocol) << 32)
+ | ((u32) a->dst_port << 16) | (a->src_port);
+ key6.key[5] = (u64) a->fib_index;
+
+ not_found =
+ clib_bihash_search_inline_48_8 (&dm->dpi6_flow_by_key, &key6);
+ p = (void *) &key6.value;
+ }
+
+ if (not_found)
+ p = 0;
+
+ if (a->is_add)
+ {
+
+ /* adding a flow entry: entry must not already exist */
+ if (p)
+ return VNET_API_ERROR_TUNNEL_EXIST;
+
+ int add_failed;
+ if (is_ip6)
+ {
+ key6.value = (u64) flow_id | ((u64) 1 << 63);
+ add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key,
+ &key6, 1 /*add */ );
+ }
+ else
+ {
+ key4.value = (u64) flow_id | ((u64) 1 << 63);
+ add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key,
+ &key4, 1 /*add */ );
+ }
+
+ if (add_failed)
+ {
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+ }
+ }
+ else
+ {
+ /* deleting a flow: flow must exist */
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ flow_id = is_ip6 ? key6.value : key4.value;
+ flow = pool_elt_at_index (dm->dpi_flows, flow_id);
+
+ if (!is_ip6)
+ clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, &key4, 0 /*del */ );
+ else
+ clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key, &key6, 0 /*del */ );
+
+ if (flow->flow_index != ~0)
+ vnet_flow_del (vnm, flow->flow_index);
+
+ pool_put (dm->dpi_flows, flow);
+ }
+
+ return 0;
+}
+
+int
+dpi_tcp_reass (tcp_reass_args_t * a)
+{
+ dpi_main_t *dm = &dpi_main;
+ dpi_flow_entry_t *flow;
+
+ flow = pool_elt_at_index (dm->dpi_flows, a->flow_id);
+ if (flow == NULL)
+ return -1;
+
+ flow->reass_en = a->reass_en;
+ flow->reass_dir = a->reass_dir;
+ return 0;
+}
+
+int
+dpi_add_del_rx_flow (u32 hw_if_index, u32 flow_id, int is_add, u32 is_ipv6)
+{
+ dpi_main_t *dm = &dpi_main;
+ vnet_main_t *vnm = dm->vnet_main;
+ dpi_flow_entry_t *dpi_flow;
+ vnet_flow_t *vent_flow;
+
+ ip_port_and_mask_t src_port;
+ ip_port_and_mask_t dst_port;
+
+
+ dpi_flow = pool_elt_at_index (dm->dpi_flows, flow_id);
+
+ src_port.port = dpi_flow->key.dst_port;
+ src_port.mask = ~0;
+ dst_port.port = dpi_flow->key.dst_port;
+ dst_port.mask = ~0;
+
+ if (is_add)
+ {
+ if (dpi_flow->flow_index == ~0)
+ {
+ if (!is_ipv6)
+ {
+ ip4_address_and_mask_t src_addr4;
+ ip4_address_and_mask_t dst_addr4;
+ src_addr4.addr = dpi_flow->key.src_ip.ip4;
+ src_addr4.mask.as_u32 = ~0;
+ dst_addr4.addr = dpi_flow->key.dst_ip.ip4;
+ dst_addr4.mask.as_u32 = ~0;
+
+ vnet_flow_t flow4 = {
+ .actions =
+ VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK,
+ .mark_flow_id = flow_id + dm->flow_id_start,
+ .redirect_node_index = 0,
+ .type = VNET_FLOW_TYPE_IP4_N_TUPLE,
+ .ip4_n_tuple = {
+ .src_addr = src_addr4,
+ .dst_addr = dst_addr4,
+ .src_port = src_port,
+ .dst_port = dst_port,
+ .protocol = dpi_flow->key.protocol,
+ }
+ ,
+ };
+ vent_flow = &flow4;
+ }
+ else
+ {
+ ip6_address_and_mask_t src_addr6;
+ ip6_address_and_mask_t dst_addr6;
+ src_addr6.addr.as_u64[0] = dpi_flow->key.src_ip.ip6.as_u64[0];
+ src_addr6.addr.as_u64[1] = dpi_flow->key.src_ip.ip6.as_u64[1];
+ src_addr6.mask.as_u64[0] = ~0;
+ src_addr6.mask.as_u64[1] = ~0;
+ dst_addr6.addr.as_u64[0] = dpi_flow->key.dst_ip.ip6.as_u64[0];
+ dst_addr6.addr.as_u64[1] = dpi_flow->key.dst_ip.ip6.as_u64[1];
+ dst_addr6.mask.as_u64[0] = ~0;
+ dst_addr6.mask.as_u64[1] = ~0;
+
+ vnet_flow_t flow6 = {
+ .actions =
+ VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK,
+ .mark_flow_id = flow_id + dm->flow_id_start,
+ .redirect_node_index = 0,
+ .type = VNET_FLOW_TYPE_IP6_N_TUPLE,
+ .ip6_n_tuple = {
+ .src_addr = src_addr6,
+ .dst_addr = dst_addr6,
+ .src_port = src_port,
+ .dst_port = dst_port,
+ .protocol = dpi_flow->key.protocol,
+ }
+ ,
+ };
+ vent_flow = &flow6;
+ }
+ vnet_flow_add (vnm, vent_flow, &(dpi_flow->flow_index));
+ }
+ return vnet_flow_enable (vnm, dpi_flow->flow_index, hw_if_index);
+ }
+
+ /* flow index is removed when the flow is deleted */
+ return vnet_flow_disable (vnm, dpi_flow->flow_index, hw_if_index);
+}
+
+void
+dpi_flow_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
+{
+ if (is_ip6)
+ vnet_feature_enable_disable ("ip6-unicast", "dpi6-input",
+ sw_if_index, is_enable, 0, 0);
+ else
+ vnet_feature_enable_disable ("ip4-unicast", "dpi4-input",
+ sw_if_index, is_enable, 0, 0);
+}
+
+int
+dpi_init_hs_database (dpi_entry_t * entry)
+{
+ u32 i, j;
+ u32 rule_num = 0;
+ unsigned char *free_list;
+ int rv;
+
+ for (i = 0;
+ (app_match_rules[i].host != NULL
+ || app_match_rules[i].pattern != NULL); i++)
+ {
+ rule_num++;
+ }
+
+ entry->expressions = (regex_t *) calloc (sizeof (char *), rule_num + 1);
+ if (entry->expressions == NULL)
+ return -1;
+
+ entry->ids = (u32 *) calloc (sizeof (u32), rule_num + 1);
+ if (entry->ids == NULL)
+ {
+ free (entry->expressions);
+ return -1;
+ }
+
+ entry->flags = (u32 *) calloc (sizeof (u32), rule_num + 1);
+ if (entry->ids == NULL)
+ {
+ free (entry->expressions);
+ free (entry->ids);
+ return -1;
+ }
+
+ free_list = (unsigned char *) calloc (sizeof (unsigned char), rule_num + 1);
+ if (free_list == NULL)
+ {
+ free (entry->expressions);
+ free (entry->ids);
+ free (entry->flags);
+ return -1;
+ }
+
+ /* first choose pattern, otherwise choose host */
+ for (i = 0, j = 0;
+ (app_match_rules[i].host != NULL
+ || app_match_rules[i].pattern != NULL); i++)
+ {
+ if (app_match_rules[i].pattern)
+ {
+ entry->expressions[j] = (regex_t) (app_match_rules[i].pattern);
+ entry->ids[j] = app_match_rules[i].app_id;
+ entry->flags[j] = HS_FLAG_SINGLEMATCH;
+ free_list[j] = 0;
+ ++j;
+ }
+ else
+ {
+ /* need to allocate additional buffer for rules */
+ entry->expressions[j] =
+ (regex_t) host2hex (app_match_rules[i].host);
+ if (entry->expressions[j] != NULL)
+ {
+ entry->ids[j] = app_match_rules[i].app_id;
+ entry->flags[j] = HS_FLAG_SINGLEMATCH;
+ free_list[j] = 1;
+ ++j;
+ }
+ }
+ }
+
+ rv = dpi_create_db_entry (entry, j, HS_MODE_STREAM);
+
+ /* Need to free additional buffers */
+ for (i = 0; i < j; ++i)
+ {
+ if (free_list[i])
+ free (entry->expressions[i]);
+ }
+
+ free (entry->expressions);
+ free (entry->ids);
+ free (entry->flags);
+ free (free_list);
+ return rv;
+}
+
+#define DPI_HASH_NUM_BUCKETS (2 * 1024)
+#define DPI_HASH_MEMORY_SIZE (1 << 20)
+
+clib_error_t *
+dpi_init (vlib_main_t * vm)
+{
+ dpi_main_t *dm = &dpi_main;
+
+ dm->vnet_main = vnet_get_main ();
+ dm->vlib_main = vm;
+
+ vnet_flow_get_range (dm->vnet_main, "dpi", 1024 * 1024, &dm->flow_id_start);
+
+ /* initialize the flow hash */
+ clib_bihash_init_24_8 (&dm->dpi4_flow_by_key, "dpi4",
+ DPI_HASH_NUM_BUCKETS, DPI_HASH_MEMORY_SIZE);
+ clib_bihash_init_48_8 (&dm->dpi6_flow_by_key, "dpi6",
+ DPI_HASH_NUM_BUCKETS, DPI_HASH_MEMORY_SIZE);
+
+ /* Init default Hyperscan database */
+ dpi_init_hs_database (&dm->default_db);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (dpi_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+// .version = VPP_BUILD_VER,
+ .description = "Deep Packet Inspection",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi.h b/src/dpi.h
new file mode 100644
index 0000000..e3d0add
--- /dev/null
+++ b/src/dpi.h
@@ -0,0 +1,330 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#ifndef included_vnet_dpi_h
+#define included_vnet_dpi_h
+
+#include <vppinfra/lock.h>
+#include <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip4_packet.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/udp/udp.h>
+#include <vnet/dpo/dpo.h>
+#include <vppinfra/bihash_8_8.h>
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_48_8.h>
+
+#include <hs/hs.h>
+#include <hs/hs_common.h>
+#include <hs/hs_compile.h>
+#include <hs/hs_runtime.h>
+#include "dpi_app_match.h"
+
+typedef u8 *regex_t;
+
+typedef struct
+{
+ u8 *name;
+ regex_t *expressions;
+ u32 *flags;
+ u32 *ids;
+ hs_database_t *database;
+ hs_scratch_t *scratch;
+ u32 ref_cnt;
+} dpi_entry_t;
+
+typedef struct
+{
+ int res;
+ u32 id;
+} dpi_cb_args_t;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+ ip46_address_t src_ip;
+ ip46_address_t dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 protocol;
+ u32 fib_index;
+ };
+ u64 key[6];
+ };
+} dpi_flow_key_t;
+
+typedef clib_bihash_kv_24_8_t dpi4_flow_key_t;
+typedef clib_bihash_kv_48_8_t dpi6_flow_key_t;
+
+typedef struct
+{
+ /* SSL */
+ u8 ssl_stage;
+ u8 ssl_got_server_cert;
+} dpi_flow_tcp_t;
+
+typedef struct
+{
+ /* TBD */
+} dpi_flow_udp_t;
+
+typedef struct segment_
+{
+ u32 send_sn;
+ u8 *data;
+ u32 len;
+ u32 bi; /* vlib buffer index */
+ struct segment_ *next;
+} segment;
+
+typedef struct tcp_stream_
+{
+ u32 send_sn; /* expected segment sn */
+ u32 ack_sn; /* acked segment sn */
+ segment *seg_queue; /* store out-of-order segments */
+} tcp_stream_t;
+
+typedef struct dpi_flow_info
+{
+ /* Required for pool_get_aligned */
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+ u16 detected_protocol[2];
+ u16 protocol_stack_info;
+ u16 pkt_num;
+ u16 pkt_direct_counter[2];
+
+ hs_stream_t *stream;
+ u8 detect_begin;
+ u8 detect_done;
+ u32 app_id; /* L7 APP ID */
+
+ u16 guessed_protocol_id;
+ u16 guessed_host_protocol_id;
+
+ u8 max_more_pkts_to_check;
+
+ int (*more_pkts_func) (u8 * payload, u32 payload_len,
+ struct dpi_flow_info * flow);
+
+ u16 dst_port;
+ u8 l4_protocol;
+ union
+ {
+ dpi_flow_tcp_t tcp;
+ dpi_flow_udp_t udp;
+ } l4;
+
+ u8 ssl_cert_detected:4;
+ u8 ssl_cert_num_checks:4;
+
+ union
+ {
+ struct
+ {
+ char server_cert[64];
+ } ssl;
+ /* TBD: Add more protocols */
+ } protos;
+} dpi_flow_info_t;
+
+typedef enum
+{
+ TCP_STATE_SYN = 1,
+ TCP_STATE_SYN_ACK = 2,
+ TCP_STATE_ACK = 3,
+ TCP_STATE_ESTABLISH = 4,
+ TCP_STATE_FIN1 = 5,
+ TCP_STATE_CLOSE = 6,
+} tcp_state_t;
+
+/* tcp packet direction */
+#define DIR_C2S 0
+#define DIR_S2C 1
+
+/* tcp reassembly side */
+#define REASS_C2S 0
+#define REASS_S2C 1
+#define REASS_BOTH 2
+
+/* Macros to handle sequence numbers */
+#define SN_LT(a,b) ((int)((a) - (b)) < 0)
+#define SN_GT(a,b) ((int)((a) - (b)) > 0)
+#define SN_EQ(a,b) ((int)((a) - (b)) == 0)
+
+typedef struct
+{
+ /* Required for pool_get_aligned */
+ CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
+ u32 flow_index; /* infra flow index */
+ u32 next_index;
+
+ u8 check_more_pkts:1;
+ u8 pkt_dir:1;
+ u8 forward_is_c2s:1;
+ u8 consumed:1;
+ u8 reass_en:1;
+ u8 reass_dir:2;
+
+ dpi_flow_key_t key;
+ dpi_flow_info_t *info;
+
+ /* TCP stream reassembly */
+ tcp_state_t tcp_state;
+ tcp_stream_t c2s;
+ tcp_stream_t s2c;
+ segment *first_seg;
+
+} dpi_flow_entry_t;
+
+typedef struct
+{
+ u32 flow_id;
+ u8 reass_en;
+ u8 reass_dir;
+} tcp_reass_args_t;
+
+typedef struct
+{
+ u8 is_add;
+ u8 is_ipv6;
+ ip46_address_t src_ip;
+ ip46_address_t dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 protocol;
+ u32 fib_index;
+} dpi_add_del_flow_args_t;
+
+typedef struct
+{
+ u32 app_id;
+ u32 db_id;
+} dpi_adr_t;
+
+typedef struct
+{
+ /* lookup tunnel by key */
+ clib_bihash_24_8_t dpi4_flow_by_key;
+ clib_bihash_48_8_t dpi6_flow_by_key;
+
+ /* vector of dpi flow instances */
+ dpi_flow_entry_t *dpi_flows;
+ u32 flow_id_start;
+ dpi_flow_info_t *dpi_infos;
+ segment *seg_pool;
+
+ /* Default hyperscan database */
+ dpi_entry_t default_db;
+
+ /* graph node state */
+ uword *bm_ip4_bypass_enabled_by_sw_if;
+ uword *bm_ip6_bypass_enabled_by_sw_if;
+
+ /* API message ID base */
+ u16 msg_id_base;
+
+ /* convenience */
+ vlib_main_t *vlib_main;
+ vnet_main_t *vnet_main;
+} dpi_main_t;
+
+extern dpi_main_t dpi_main;
+
+#define foreach_copy_field \
+_(src_ip) \
+_(dst_ip) \
+_(src_port) \
+_(dst_port) \
+_(protocol) \
+_(fib_index)
+
+
+#define dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0) \
+while(seg) \
+ { \
+ bi0 = seg->bi; \
+ to_next[0] = bi0; \
+ to_next++; \
+ n_left_to_next--; \
+ next0 = flow0->next_index; \
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index, \
+ to_next, n_left_to_next, \
+ bi0, next0); \
+ prev_seg = seg; \
+ seg=seg->next; \
+ pool_put(dm->seg_pool, prev_seg); \
+ }
+
+
+#define get_u16_t(X,O) (*(u16 *)(((u8 *)X) + O))
+#define DPI_MAX_SSL_REQUEST_SIZE 10000
+
+int dpi_flow_add_del (dpi_add_del_flow_args_t * a, u32 * flow_idp);
+int dpi_reverse_flow_add_del (dpi_add_del_flow_args_t * a, u32 flow_id);
+int dpi_add_del_rx_flow (u32 hw_if_index, u32 flow_id, int is_add,
+ u32 is_ipv6);
+int dpi_tcp_reass (tcp_reass_args_t * a);
+void dpi_flow_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable);
+int dpi_search_host_protocol (dpi_flow_info_t * flow,
+ char *str_to_match,
+ u32 str_to_match_len,
+ u16 master_protocol_id, u32 * host_protocol_id);
+void dpi_search_tcp_ssl (u8 * payload, u32 payload_len,
+ dpi_flow_info_t * flow);
+
+void dpi_detect_application (u8 * payload, u32 payload_len,
+ dpi_flow_info_t * flow);
+
+typedef enum
+{
+ DPI_PROTOCOL_UNKNOWN = 0,
+ DPI_PROTOCOL_SSL = 1,
+ DPI_PROTOCOL_SSL_NO_CERT = 2,
+ DPI_N_PROTOCOL
+} dpi_protocol_id_t;
+
+#define foreach_dpi_input_next \
+_(DROP, "error-drop") \
+_(IP4_LOOKUP, "ip4-lookup")
+
+typedef enum
+{
+#define _(s,n) DPI_INPUT_NEXT_##s,
+ foreach_dpi_input_next
+#undef _
+ DPI_INPUT_N_NEXT,
+} dpi_input_next_t;
+
+#endif /* included_vnet_dpi_h */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi_api.c b/src/dpi_api.c
new file mode 100644
index 0000000..d8fa690
--- /dev/null
+++ b/src/dpi_api.c
@@ -0,0 +1,160 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+*
+*/
+
+#include <vnet/interface.h>
+#include <vnet/api_errno.h>
+#include <vnet/feature/feature.h>
+#include <vnet/fib/fib_table.h>
+
+#include <vppinfra/byte_order.h>
+#include <vlibmemory/api.h>
+
+#include "dpi.h"
+
+
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include "dpi.api.h"
+ /* We'll want to know how many messages IDs we need... */
+ VL_MSG_FIRST_AVAILABLE,
+} vl_msg_id_t;
+#undef vl_msg_id
+
+/* define message structures */
+#define vl_typedefs
+#include "dpi.api.h"
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include "dpi.api.h"
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include "dpi.api.h"
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include "dpi.api.h"
+#undef vl_api_version
+
+#define vl_msg_name_crc_list
+#include "dpi.api.h"
+#undef vl_msg_name_crc_list
+
+#define REPLY_MSG_ID_BASE dm->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+static void
+setup_message_id_table (dpi_main_t * dm, api_main_t * am)
+{
+#define _(id,n,crc) \
+ vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + dm->msg_id_base);
+ foreach_vl_msg_name_crc_dpi;
+#undef _
+}
+
+#define foreach_dpi_plugin_api_msg \
+_(DPI_FLOW_ADD_DEL, dpi_flow_add_del)
+
+
+/* API message handler */
+static void
+vl_api_dpi_flow_add_del_t_handler (vl_api_dpi_flow_add_del_t * mp)
+{
+ vl_api_dpi_flow_add_del_reply_t *rmp = NULL;
+ dpi_main_t *dm = &dpi_main;
+ int rv = 0;
+ u32 fib_index;
+ u32 flow_id = ~0;
+
+ fib_index = fib_table_find (fib_ip_proto (mp->is_ipv6), ntohl (mp->vrf_id));
+ if (fib_index == ~0)
+ {
+ rv = VNET_API_ERROR_NO_SUCH_FIB;
+ goto out;
+ }
+
+ dpi_add_del_flow_args_t a = {
+ .is_add = mp->is_add,
+ .is_ipv6 = mp->is_ipv6,
+ .src_ip = to_ip46 (mp->is_ipv6, mp->src_ip),
+ .dst_ip = to_ip46 (mp->is_ipv6, mp->dst_ip),
+ .src_port = ntohs (mp->src_port),
+ .dst_port = ntohs (mp->dst_port),
+ .protocol = mp->protocol,
+ .fib_index = fib_index,
+ };
+
+ /* Check src ip and dst ip are different */
+ if (ip46_address_cmp (&a.dst_ip, &a.src_ip) == 0)
+ {
+ rv = VNET_API_ERROR_SAME_SRC_DST;
+ goto out;
+ }
+
+ rv = dpi_flow_add_del (&a, &flow_id);
+
+out:
+ /* *INDENT-OFF* */
+ REPLY_MACRO2(VL_API_DPI_FLOW_ADD_DEL_REPLY,
+ ({
+ rmp->flow_id = htonl (flow_id);
+ }));
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+dpi_api_hookup (vlib_main_t * vm)
+{
+ dpi_main_t *dm = &dpi_main;
+
+ u8 *name = format (0, "dpi_%08x%c", api_version, 0);
+ dm->msg_id_base = vl_msg_api_get_msg_ids
+ ((char *) name, VL_MSG_FIRST_AVAILABLE);
+
+#define _(N,n) \
+ vl_msg_api_set_handlers((VL_API_##N + dm->msg_id_base), \
+ #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_dpi_plugin_api_msg;
+#undef _
+
+ /* Add our API messages to the global name_crc hash table */
+ setup_message_id_table (dm, &api_main);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (dpi_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi_app_match.h b/src/dpi_app_match.h
new file mode 100644
index 0000000..dde4857
--- /dev/null
+++ b/src/dpi_app_match.h
@@ -0,0 +1,124 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2019 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+/*
+#*************************************************************
+# Copyright (c) 2003-2017, Emerging Threats
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+# following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+# disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other materials provided with the distribution.
+# * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#*************************************************************
+*/
+
+#ifndef dpi_app_match_h
+#define dpi_app_match_h
+
+typedef enum
+{
+ DPI_APP_CISCO = 1,
+ DPI_APP_GOOGLE = 2,
+ DPI_APP_BING = 3,
+ DPI_APP_MSN = 4,
+ DPI_APP_YAHOO = 5,
+ DPI_APP_YAHOOMAIL = 6,
+ DPI_APP_INTEL = 7,
+ DPI_APP_AMAZON = 8,
+ DPI_APP_AMD = 9,
+ DPI_APP_BAIDU = 10,
+ DPI_APP_APPLE = 11,
+ DPI_APP_FACEBOOK = 12,
+ DPI_APP_EBAY = 13,
+ DPI_APP_GITHUB = 14,
+ DPI_APP_GMAIL = 15,
+ DPI_APP_QQ = 16,
+ DPI_APP_WECHAT = 17,
+ DPI_APP_PINTEREST = 18,
+ DPI_APP_LENOVO = 19,
+ DPI_APP_LINKEDIN = 20,
+ DPI_APP_SKYPE = 21,
+ DPI_APP_MICROSOFT = 22,
+ DPI_APP_NETFLIX = 23,
+ DPI_APP_NOKIA = 24,
+ DPI_APP_NVIDIA = 25,
+ DPI_APP_OFFICE = 26,
+ DPI_APP_ORACLE = 27,
+ DPI_APP_OUTLOOK = 28,
+ DPI_APP_PANDORA = 29,
+ DPI_APP_PAYPAL = 30,
+ DPI_APP_SINA = 31,
+ DPI_APP_SOGOU = 32,
+ DPI_APP_SYMANTEC = 33,
+ DPI_APP_TAOBAO = 34,
+ DPI_APP_TWITTER = 35,
+ DPI_APP_UPS = 36,
+ DPI_APP_VISA = 37,
+ DPI_APP_MCAFEE = 38,
+ DPI_APP_VMWARE = 39,
+ DPI_APP_WORDPRESS = 40,
+ DPI_APP_ADOBE = 41,
+ DPI_APP_AKAMAI = 42,
+ DPI_APP_ALIENVAULT = 43,
+ DPI_APP_BITCOMET = 44,
+ DPI_APP_CHECKPOINT = 45,
+ DPI_APP_BLOOMBERG = 46,
+ DPI_APP_DELL = 47,
+ DPI_APP_F5 = 48,
+ DPI_APP_FIREEYE = 49,
+ DPI_APP_DROPBOX = 50,
+
+ /* last app ID */
+ DPI_N_APPLICATIONS = 51,
+} dpi_application_id_t;
+
+typedef struct dpi_app_match_rule_
+{
+ char *host;
+ char *pattern;
+ char *app_name;
+ u32 app_id;
+} dpi_app_match_rule;
+
+#define DPI_MAX_APP_NUM DPI_N_APPLICATIONS
+extern dpi_app_match_rule app_match_rules[];
+
+
+#endif /* dpi_app_match_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi_cli.c b/src/dpi_cli.c
new file mode 100644
index 0000000..4fbc760
--- /dev/null
+++ b/src/dpi_cli.c
@@ -0,0 +1,357 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Intel, Travelping and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <stdint.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+//#include <vpp/app/version.h>
+#include "dpi.h"
+
+
+extern dpi_main_t dpi_main;
+extern dpi_entry_t *dpi_dbs;
+
+static clib_error_t *
+dpi_flow_add_del_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd_arg)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ ip46_address_t src_ip = ip46_address_initializer;
+ ip46_address_t dst_ip = ip46_address_initializer;
+ u16 src_port = 0, dst_port = 0;
+ u8 is_add = 0;
+ u8 ipv4_set = 0;
+ u8 ipv6_set = 0;
+ u32 tmp;
+ int rv;
+ u8 protocol = 0;
+ u32 table_id;
+ u32 fib_index = 0;
+ u32 dpi_flow_id;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "add"))
+ is_add = 1;
+ else if (unformat (line_input, "del"))
+ is_add = 0;
+ else if (unformat (line_input, "src-ip %U", unformat_ip46_address,
+ &src_ip, IP46_TYPE_ANY))
+ {
+ ip46_address_is_ip4 (&src_ip) ? (ipv4_set = 1) : (ipv6_set = 1);
+ }
+ else if (unformat (line_input, "dst-ip %U", unformat_ip46_address,
+ &dst_ip, IP46_TYPE_ANY))
+ {
+ ip46_address_is_ip4 (&dst_ip) ? (ipv4_set = 1) : (ipv6_set = 1);
+ }
+ else if (unformat (line_input, "src-port %d", &tmp))
+ src_port = (u16) tmp;
+ else if (unformat (line_input, "dst-port %d", &tmp))
+ dst_port = (u16) tmp;
+ else
+ if (unformat (line_input, "protocol %U", unformat_ip_protocol, &tmp))
+ protocol = (u8) tmp;
+ else if (unformat (line_input, "protocol %u", &tmp))
+ protocol = (u8) tmp;
+ else if (unformat (line_input, "vrf-id %d", &table_id))
+ {
+ fib_index = fib_table_find (fib_ip_proto (ipv6_set), table_id);
+ }
+ else
+ return clib_error_return (0, "parse error: '%U'",
+ format_unformat_error, line_input);
+ }
+
+ unformat_free (line_input);
+
+ if (ipv4_set && ipv6_set)
+ return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+
+ dpi_add_del_flow_args_t a = {.is_add = is_add,
+ .is_ipv6 = ipv6_set,
+#define _(x) .x = x,
+ foreach_copy_field
+#undef _
+ };
+
+ /* Add normal flow */
+ rv = dpi_flow_add_del (&a, &dpi_flow_id);
+ if (rv < 0)
+ return clib_error_return (0, "flow error: %d", rv);
+
+ /* Add reverse flow */
+ rv = dpi_reverse_flow_add_del (&a, dpi_flow_id);
+ if (rv < 0)
+ return clib_error_return (0, "reverse flow error: %d", rv);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dpi_flow_add_del_command, static) = {
+ .path = "dpi flow",
+ .short_help = "dpi flow [add | del] "
+ "[src-ip <ip-addr>] [dst-ip <ip-addr>] "
+ "[src-port <port>] [dst-port <port>] "
+ "[protocol <protocol>] [vrf-id <nn>]",
+ .function = dpi_flow_add_del_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+dpi_tcp_reass_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd_arg)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 flow_id = ~0;
+ u8 reass_en = 0;
+ u8 reass_dir = 0;
+ int rv;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "flow_id %d", &flow_id))
+ ;
+ else if (unformat (line_input, "enable"))
+ {
+ reass_en = 1;
+ }
+ else if (unformat (line_input, "disable"))
+ {
+ reass_en = 0;
+ }
+ else if (unformat (line_input, "client"))
+ {
+ reass_dir = REASS_C2S;
+ }
+ else if (unformat (line_input, "server"))
+ {
+ reass_dir = REASS_S2C;
+ }
+ else if (unformat (line_input, "both"))
+ {
+ reass_dir = REASS_BOTH;
+ }
+ else
+ return clib_error_return (0, "parse error: '%U'",
+ format_unformat_error, line_input);
+ }
+
+ unformat_free (line_input);
+
+ tcp_reass_args_t a = {.flow_id = flow_id,
+ .reass_en = reass_en,
+ .reass_dir = reass_dir,
+ };
+
+ rv = dpi_tcp_reass (&a);
+ if (rv < 0)
+ return clib_error_return (0, "flow error: %d", rv);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dpi_tcp_reass_command, static) = {
+ .path = "dpi tcp reass",
+ .short_help = "dpi tcp reass flow_id <nn> <enable|disable> "
+ "[ <client | server | both> ]",
+ .function = dpi_tcp_reass_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+dpi_flow_offload_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ dpi_main_t *dm = &dpi_main;
+ vnet_main_t *vnm = dm->vnet_main;
+ u32 rx_flow_id = ~0;
+ u32 hw_if_index = ~0;
+ int is_add = 1;
+ u32 is_ipv6 = 0;
+ dpi_flow_entry_t *flow;
+ vnet_hw_interface_t *hw_if;
+ u32 rx_fib_index = ~0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm,
+ &hw_if_index))
+ continue;
+ if (unformat (line_input, "rx %d", &rx_flow_id))
+ continue;
+ if (unformat (line_input, "del"))
+ {
+ is_add = 0;
+ continue;
+ }
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ }
+
+ if (rx_flow_id == ~0)
+ return clib_error_return (0, "missing rx flow");
+ if (hw_if_index == ~0)
+ return clib_error_return (0, "missing hw interface");
+
+ flow = pool_elt_at_index (dm->dpi_flows, rx_flow_id);
+
+ hw_if = vnet_get_hw_interface (vnm, hw_if_index);
+
+ is_ipv6 = ip46_address_is_ip4 (&(flow->key.src_ip)) ? 0 : 1;
+
+ if (is_ipv6)
+ {
+ ip6_main_t *im6 = &ip6_main;
+ rx_fib_index =
+ vec_elt (im6->fib_index_by_sw_if_index, hw_if->sw_if_index);
+ }
+ else
+ {
+ ip4_main_t *im4 = &ip4_main;
+ rx_fib_index =
+ vec_elt (im4->fib_index_by_sw_if_index, hw_if->sw_if_index);
+ }
+
+ if (flow->key.fib_index != rx_fib_index)
+ return clib_error_return (0, "interface/flow fib mismatch");
+
+ if (dpi_add_del_rx_flow (hw_if_index, rx_flow_id, is_add, is_ipv6))
+ return clib_error_return (0, "error %s flow",
+ is_add ? "enabling" : "disabling");
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dpi_flow_offload_command, static) = {
+ .path = "dpi set flow-offload",
+ .short_help =
+ "dpi set flow-offload hw <interface-name> rx <flow-id> [del]",
+ .function = dpi_flow_offload_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+dpi_set_flow_bypass (u32 is_ip6,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ vnet_main_t *vnm = vnet_get_main ();
+ clib_error_t *error = 0;
+ u32 sw_if_index, is_enable;
+
+ sw_if_index = ~0;
+ is_enable = 1;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat_user (line_input, unformat_vnet_sw_interface, vnm,
+ &sw_if_index))
+ ;
+ else if (unformat (line_input, "del"))
+ is_enable = 0;
+ else
+ {
+ error = unformat_parse_error (line_input);
+ goto done;
+ }
+ }
+
+ if (~0 == sw_if_index)
+ {
+ error = clib_error_return (0, "unknown interface `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+
+ dpi_flow_bypass_mode (sw_if_index, is_ip6, is_enable);
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static clib_error_t *
+dpi_set_ip4_flow_bypass_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ return dpi_set_flow_bypass (0, input, cmd);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dpi_set_ip4_flow_bypass_command, static) =
+{
+ .path = "dpi set ip4 flow-bypass",
+ .short_help = "dpi set ip4 flow-bypass <interface> [del]",
+ .function = dpi_set_ip4_flow_bypass_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+dpi_set_ip6_flow_bypass_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ return dpi_set_flow_bypass (0, input, cmd);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dpi_set_ip6_flow_bypass_command, static) =
+{
+ .path = "dpi set ip6 flow-bypass",
+ .short_help = "dpi set ip6 flow-bypass <interface> [del]",
+ .function = dpi_set_ip6_flow_bypass_command_fn,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi_node.c b/src/dpi_node.c
new file mode 100644
index 0000000..6b5f9c1
--- /dev/null
+++ b/src/dpi_node.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2019 Intel and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/pg/pg.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vppinfra/bihash_48_8.h>
+#include <vppinfra/dlist.h>
+#include <vppinfra/pool.h>
+#include <vppinfra/vec.h>
+#include <vnet/plugin/plugin.h>
+//#include <vpp/app/version.h>
+#include <vnet/flow/flow.h>
+#include <vnet/tcp/tcp_packet.h>
+
+#include "dpi.h"
+
+vlib_node_registration_t dpi4_input_node;
+vlib_node_registration_t dpi6_input_node;
+vlib_node_registration_t dpi4_flow_input_node;
+vlib_node_registration_t dpi6_flow_input_node;
+
+
+#define foreach_dpi_input_error \
+ _(NONE, "no error") \
+ _(NO_SUCH_FLOW, "flow not existed")
+
+typedef enum
+{
+#define _(sym,str) DPI_INPUT_ERROR_##sym,
+ foreach_dpi_input_error
+#undef _
+ DPI_INPUT_N_ERROR,
+} dpi_input_error_t;
+
+static char *dpi_input_error_strings[] = {
+#define _(sym,string) string,
+ foreach_dpi_input_error
+#undef _
+};
+
+typedef struct
+{
+ u32 next_index;
+ u32 flow_id;
+ u32 app_id;
+ u32 error;
+} dpi_rx_trace_t;
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (dpi4_input, static) =
+{
+ .arc_name = "ip4-unicast",
+ .node_name = "dpi4-input",
+ .runs_before = VNET_FEATURES ("ip4-lookup"),
+};
+
+VNET_FEATURE_INIT (dpi6_input, static) =
+{
+ .arc_name = "ip6-unicast",
+ .node_name = "dpi6-input",
+ .runs_before = VNET_FEATURES ("ip6-lookup"),
+};
+/* *INDENT-on* */
+
+static u8 *
+format_dpi_rx_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ dpi_rx_trace_t *t = va_arg (*args, dpi_rx_trace_t *);
+
+ if (t->flow_id == ~0)
+ return format (s, "DPI error - flow %d does not exist",
+ t->flow_id);
+
+ return format (s, "DPI from flow %d app_id %d next %d error %d",
+ t->flow_id, t->app_id, t->next_index, t->error);
+}
+
+
+
+static inline int
+parse_ip4_packet_and_lookup (ip4_header_t * ip4, u32 fib_index,
+ dpi4_flow_key_t * key4,
+ int * not_found, u64 * flow_id)
+{
+ dpi_main_t *dm = &dpi_main;
+ u8 protocol = ip4_is_fragment (ip4) ? 0xfe : ip4->protocol;
+ u16 src_port = 0;
+ u16 dst_port = 0;
+ dpi_flow_entry_t *flow;
+
+ key4->key[0] = ip4->src_address.as_u32
+ | (((u64) ip4->dst_address.as_u32) << 32);
+
+ if (protocol == IP_PROTOCOL_UDP || protocol == IP_PROTOCOL_TCP)
+ {
+ /* tcp and udp ports have the same offset */
+ udp_header_t * udp = ip4_next_header (ip4);
+ src_port = udp->src_port;
+ dst_port = udp->dst_port;
+ }
+
+ key4->key[1] = (((u64) protocol) << 32) | ((u32) src_port << 16) | dst_port;
+ key4->key[2] = (u64) fib_index;
+
+ key4->value = ~0;
+ *not_found = clib_bihash_search_inline_24_8 (&dm->dpi4_flow_by_key, key4);
+ *flow_id = key4->value;
+
+ /* not found, then create new SW flow dynamically */
+ if (*not_found)
+ {
+ int add_failed;
+ pool_get_aligned(dm->dpi_flows, flow, CLIB_CACHE_LINE_BYTES);
+ clib_memset(flow, 0, sizeof(*flow));
+ *flow_id = flow - dm->dpi_flows;
+
+ flow->next_index = DPI_INPUT_NEXT_IP4_LOOKUP;
+ flow->flow_index = ~0;
+
+ pool_get_aligned(dm->dpi_infos, flow->info, CLIB_CACHE_LINE_BYTES);
+ clib_memset(flow->info, 0, sizeof(*flow->info));
+ flow->info->app_id = ~0;
+
+ /* Add forwarding flow entry */
+ key4->value = *flow_id;
+ add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, key4,
+ 1 /*add */);
+
+ if (add_failed)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+
+ /* Add reverse flow entry*/
+ key4->key[0] = ip4->dst_address.as_u32
+ | (((u64) ip4->src_address.as_u32) << 32);
+ key4->key[1] = (((u64) protocol) << 32) | ((u32) dst_port << 16)
+ | src_port;
+ key4->key[2] = (u64) fib_index;
+ key4->value = (u64) flow_id | ((u64) 1 << 63);
+ add_failed = clib_bihash_add_del_24_8 (&dm->dpi4_flow_by_key, key4,
+ 1 /*add */);
+
+ if (add_failed)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+
+ /* Open a Hyperscan stream for each flow */
+ hs_error_t err = hs_open_stream (dm->default_db.database, 0,
+ &(flow->info->stream));
+
+ if (err != HS_SUCCESS)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int
+parse_ip6_packet_and_lookup (ip6_header_t * ip6, u32 fib_index,
+ dpi6_flow_key_t * key6,
+ int * not_found, u64 * flow_id)
+{
+ dpi_main_t *dm = &dpi_main;
+ u8 protocol = ip6->protocol;
+ u16 src_port = 0;
+ u16 dst_port = 0;
+ dpi_flow_entry_t *flow;
+
+ key6->key[0] = ip6->src_address.as_u64[0];
+ key6->key[1] = ip6->src_address.as_u64[1];
+ key6->key[2] = ip6->dst_address.as_u64[0];
+ key6->key[3] = ip6->dst_address.as_u64[1];
+
+ if (protocol == IP_PROTOCOL_UDP || protocol == IP_PROTOCOL_TCP)
+ {
+ /* tcp and udp ports have the same offset */
+ udp_header_t * udp = ip6_next_header(ip6);
+ src_port = udp->src_port;
+ dst_port = udp->dst_port;
+ }
+
+ key6->key[4] = (((u64) protocol) << 32)
+ | ((u32) src_port << 16)
+ | dst_port;
+ key6->key[5] = (u64) fib_index;
+
+ key6->value = ~0;
+ *not_found = clib_bihash_search_inline_48_8 (&dm->dpi6_flow_by_key, key6);
+ *flow_id = key6->value;
+
+ /* not found, then create new SW flow dynamically */
+ if (*not_found)
+ {
+ int add_failed;
+ pool_get_aligned(dm->dpi_flows, flow, CLIB_CACHE_LINE_BYTES);
+ clib_memset(flow, 0, sizeof(*flow));
+ *flow_id = flow - dm->dpi_flows;
+
+ flow->next_index = DPI_INPUT_NEXT_IP4_LOOKUP;
+ flow->flow_index = ~0;
+
+ pool_get_aligned(dm->dpi_infos, flow->info, CLIB_CACHE_LINE_BYTES);
+ clib_memset(flow->info, 0, sizeof(*flow->info));
+ flow->info->app_id = ~0;
+
+ /* Add forwarding flow entry */
+ key6->value = (u64) flow_id;
+ add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key,
+ key6, 1 /*add */ );
+ if (add_failed)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+
+ /* Add reverse flow entry*/
+ key6->key[0] = ip6->dst_address.as_u64[0];
+ key6->key[1] = ip6->dst_address.as_u64[1];
+ key6->key[2] = ip6->src_address.as_u64[0];
+ key6->key[3] = ip6->src_address.as_u64[1];
+ key6->key[4] = (((u64) protocol) << 32)
+ | ((u32) dst_port << 16)
+ | src_port;
+ key6->key[5] = (u64) fib_index;
+ key6->value = (u64) flow_id | ((u64) 1 << 63);
+ add_failed = clib_bihash_add_del_48_8 (&dm->dpi6_flow_by_key,
+ key6, 1 /*add */ );
+
+ if (add_failed)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+
+ /* Open a Hyperscan stream for each flow */
+ hs_error_t err = hs_open_stream (dm->default_db.database, 0,
+ &(flow->info->stream));
+
+ if (err != HS_SUCCESS)
+ {
+ pool_put(dm->dpi_infos, flow->info);
+ pool_put(dm->dpi_flows, flow);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline void
+dpi_trim_overlap(u32 left_sn, segment *seg)
+{
+ int overlap_len;
+
+ overlap_len = left_sn - seg->send_sn;
+ /* trim leading overlap bytes */
+ seg->data += overlap_len;
+ seg->len -= overlap_len;
+ seg->send_sn += overlap_len;
+
+ /* trim the right overlap bytes */
+ if( seg->next
+ && (seg->send_sn+seg->len) > (seg->next->send_sn) )
+ {
+ overlap_len = (seg->send_sn+seg->len) - (seg->next->send_sn);
+ if(seg->len > overlap_len)
+ {
+ seg->len -= overlap_len;
+ }
+ }
+}
+
+/*
+ * re-order out-of-order segments, and handle overlap segments.
+ * */
+static inline void
+dpi_handle_tcp_segments (dpi_flow_entry_t *flow, tcp_stream_t *stream,
+ u32 bi, u8 *pkt, u32 payload_len)
+{
+ dpi_main_t *dm = &dpi_main;
+ u32 send_sn;
+ u32 ack_sn;
+ u32 next_sn;
+ u32 left_sn;
+ segment *first_seg = 0;
+ segment *seg = 0;
+ segment *new_seg = 0;
+ tcp_header_t *tcp = (tcp_header_t *)pkt;
+ u8 *payload = pkt + tcp_doff(tcp) * 4;
+
+ if((tcp->flags & TCP_FLAG_ACK) == TCP_FLAG_ACK)
+ {
+ ack_sn = clib_net_to_host_u32(tcp->ack_number);
+ if(ack_sn != stream->ack_sn)
+ {
+ stream->ack_sn = ack_sn;
+ }
+ }
+
+ send_sn = clib_net_to_host_u32(tcp->seq_number);
+ next_sn = send_sn + payload_len;
+
+ /* Handle fully overlapping segments */
+ if(SN_GT(stream->send_sn, next_sn))
+ {
+ flow->consumed = 1;
+ return;
+ }
+
+ if(SN_LT(stream->send_sn, send_sn))
+ {
+ /* Store out-of-order segments to segment queue */
+ for(seg=stream->seg_queue; seg; seg=seg->next)
+ {
+ if (send_sn < seg->send_sn )
+ break;
+ }
+
+ pool_get_aligned (dm->seg_pool, new_seg, CLIB_CACHE_LINE_BYTES);
+ new_seg->bi = bi;
+ new_seg->send_sn = send_sn;
+ new_seg->data = payload;
+ new_seg->len = payload_len;
+
+ /* Insert new segment to right position of segment queue */
+ if(seg == stream->seg_queue)
+ {
+ new_seg->next = stream->seg_queue;
+ stream->seg_queue = new_seg;
+ stream->send_sn = seg->send_sn;
+ left_sn = stream->send_sn;
+ }
+ else
+ {
+ new_seg->next = seg->next;
+ seg->next = new_seg;
+ left_sn = seg->send_sn;
+ }
+
+ /* trim overlapped packet */
+ dpi_trim_overlap(left_sn, new_seg);
+
+ flow->consumed = 1;
+ }
+ else
+ {
+ pool_get_aligned(dm->seg_pool, first_seg, CLIB_CACHE_LINE_BYTES);
+ first_seg->bi = bi;
+ first_seg->send_sn = send_sn;
+ first_seg->data = payload;
+ first_seg->len = payload_len;
+ first_seg->next = stream->seg_queue;
+
+ /* trim overlapped packet */
+ dpi_trim_overlap (stream->send_sn, first_seg);
+
+ /* reassemble continuous segments and move forward to scan */
+ for (seg = first_seg; seg->next; seg = seg->next)
+ {
+ if (seg->send_sn + seg->len != seg->next->send_sn)
+ break;
+ }
+
+ /* left non-continuous segments */
+ stream->seg_queue = seg->next;
+ stream->send_sn = seg->send_sn + seg->len;
+
+ flow->first_seg = first_seg;
+ seg->next = 0;
+
+ /* scan ordered segments */
+ for (seg = first_seg; seg; seg = seg->next)
+ {
+ /* detect layer 7 application for single segment */
+ dpi_detect_application (seg->data, seg->len, flow->info);
+ if(flow->info->detect_done)
+ break;
+ }
+ }
+}
+
+static inline int
+dpi_handle_tcp_stream (dpi_flow_entry_t *flow, u32 bi,
+ u8 *pkt, u32 payload_len, u8 is_reverse)
+{
+ tcp_header_t *tcp;
+ tcp_stream_t *stream;
+
+ tcp = (tcp_header_t *)pkt;
+ if((tcp->flags & (TCP_FLAG_SYN|TCP_FLAG_ACK)) == TCP_FLAG_SYN)
+ {
+ flow->c2s.send_sn = clib_net_to_host_u32(tcp->seq_number) + 1;
+ flow->pkt_dir = DIR_C2S;
+ flow->forward_is_c2s = !is_reverse;
+ flow->tcp_state = TCP_STATE_SYN;
+ }
+ else
+ {
+ /*
+ forward_is_c2s | is_reverse
+ 0 1
+ 0 s2c(1) c2s(0)
+ 1 c2s(0) s2c(1)
+ */
+ flow->pkt_dir = (flow->forward_is_c2s == is_reverse);
+ }
+
+ switch(flow->tcp_state)
+ {
+ case TCP_STATE_SYN:
+ {
+ if(flow->pkt_dir != DIR_S2C)
+ break;
+
+ if((tcp->flags & (TCP_FLAG_SYN|TCP_FLAG_ACK))
+ != (TCP_FLAG_SYN|TCP_FLAG_ACK))
+ break;
+
+ flow->s2c.send_sn = clib_net_to_host_u32(tcp->seq_number) + 1;
+ flow->s2c.ack_sn = clib_net_to_host_u32(tcp->ack_number) + 1;
+ flow->tcp_state = TCP_STATE_SYN_ACK;
+ break;
+ }
+
+ case TCP_STATE_SYN_ACK:
+ {
+ if(flow->pkt_dir != DIR_C2S)
+ break;
+
+ flow->c2s.ack_sn = clib_net_to_host_u32(tcp->ack_number) + 1;
+ flow->tcp_state = TCP_STATE_ESTABLISH;
+ break;
+ }
+
+ case TCP_STATE_ACK:
+ case TCP_STATE_ESTABLISH:
+ case TCP_STATE_FIN1:
+ {
+ stream = (flow->pkt_dir == DIR_C2S)? &(flow->c2s) : &(flow->s2c);
+ if( (flow->reass_dir == REASS_BOTH)
+ || ((flow->pkt_dir==DIR_C2S) && (flow->reass_dir==REASS_C2S))
+ || ((flow->pkt_dir==DIR_S2C) && (flow->reass_dir==REASS_S2C)) )
+ {
+ dpi_handle_tcp_segments(flow, stream, bi, pkt, payload_len);
+ }
+
+ break;
+ }
+
+ case TCP_STATE_CLOSE:
+ {
+ /* Free all segments in the queue */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void
+dpi_detect_application (u8 *payload, u32 payload_len,
+ dpi_flow_info_t *flow)
+{
+
+ /* detect if payload is SSL's payload for default port */
+ dpi_search_tcp_ssl(payload, payload_len, flow);
+
+ /* TBD: add detect if is SSL's payload with non default port*/
+
+}
+
+always_inline uword
+dpi_input_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, u32 is_ip4)
+{
+ dpi_main_t *dm = &dpi_main;
+ u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, next0 = 0;
+ vlib_buffer_t *b0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ tcp_header_t *tcp0;
+ udp_header_t *udp0;
+ dpi4_flow_key_t key40;
+ dpi6_flow_key_t key60;
+ u32 fib_index0 = ~0;
+ u64 flow_id0 = ~0;
+ u32 flow_index0 = ~0;
+ int not_found0 = 0;
+ u8 is_reverse0 = 0;
+ dpi_flow_entry_t *flow0 = 0;
+ u32 ip_len0, l4_len0, payload_len0;
+ u8 protocol0;
+ u8 *l4_pkt0, *payload0;
+ u16 dst_port = 0;
+ segment *seg = 0;
+ segment *prev_seg = 0;
+
+ bi0 = to_next[0] = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+ ip_len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (is_ip4)
+ {
+ ip40 = vlib_buffer_get_current (b0);
+ ip4_main_t *im4 = &ip4_main;
+ fib_index0 = vec_elt (im4->fib_index_by_sw_if_index,
+ vnet_buffer(b0)->sw_if_index[VLIB_RX]);
+ parse_ip4_packet_and_lookup(ip40, fib_index0, &key40,
+ &not_found0, &flow_id0);
+ }
+ else
+ {
+ ip60 = vlib_buffer_get_current (b0);
+ ip6_main_t *im6 = &ip6_main;
+ fib_index0 = vec_elt (im6->fib_index_by_sw_if_index,
+ vnet_buffer(b0)->sw_if_index[VLIB_RX]);
+ parse_ip6_packet_and_lookup(ip60, fib_index0, &key60,
+ &not_found0, &flow_id0);
+ }
+
+ is_reverse0 = (u8)((flow_id0 >> 63) & 0x1);
+ flow_index0 = (u32)(flow_id0 & (u32)(~0));
+ flow0 = pool_elt_at_index (dm->dpi_flows, flow_index0);
+ /* have detected successfully, directly return */
+ if(flow0->info->detect_done)
+ goto enqueue0;
+
+ /* check layer4 */
+ if (is_ip4)
+ {
+ l4_pkt0 = (u8 *)(ip40 + 1);
+ l4_len0 = ip_len0 - sizeof(ip4_header_t);
+ protocol0 = ip40->protocol;
+ }
+ else
+ {
+ l4_pkt0 = (u8 *)(ip60 + 1);
+ l4_len0 = ip_len0 - sizeof(ip6_header_t);
+ protocol0 = ip60->protocol;
+ }
+
+ if((protocol0 == IP_PROTOCOL_TCP) && (l4_len0 >= 20))
+ {
+ tcp0 = (tcp_header_t *)l4_pkt0;
+ payload_len0 = l4_len0 - tcp_doff(tcp0) * 4;
+ payload0 = l4_pkt0 + tcp_doff(tcp0) * 4;
+ dst_port = tcp0->dst_port;
+ }
+ else if ((protocol0 == IP_PROTOCOL_UDP) && (l4_len0 >= 8))
+ {
+ udp0 = (udp_header_t *)l4_pkt0;
+ payload_len0 = l4_len0 - sizeof(udp_header_t);
+ payload0 = l4_pkt0 + sizeof(udp_header_t);
+ dst_port = udp0->dst_port;
+ }
+ else
+ {
+ payload_len0 = l4_len0;
+ payload0 = l4_pkt0;
+ }
+
+ flow0->info->l4_protocol = protocol0;
+ flow0->info->dst_port = dst_port;
+
+ /* TCP stream reassembly and detect a protocol pdu */
+ if((protocol0 == IP_PROTOCOL_TCP) && (flow0->reass_en))
+ {
+ dpi_handle_tcp_stream(flow0, bi0, l4_pkt0, payload_len0, is_reverse0);
+
+ /* This packet has been consumed, retrieve next packet */
+ if(flow0->consumed)
+ goto trace0;
+
+ /* send out continuous scanned segments */
+ seg=flow0->first_seg;
+ dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0);
+ flow0->first_seg = 0;
+
+ /* Here detected successfully, send out remaining segments in seg_queue */
+ if(flow0->info->detect_done)
+ {
+ seg=flow0->c2s.seg_queue;
+ dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0);
+ flow0->c2s.seg_queue = 0;
+
+ seg=flow0->s2c.seg_queue;
+ dpi_enqueue_tcp_segments(seg,vm,node,next_index,to_next,n_left_to_next,bi0,next0);
+ flow0->s2c.seg_queue = 0;
+ }
+ goto trace0;
+ }
+ else
+ {
+ /* detect layer 7 application for single packet */
+ dpi_detect_application (payload0, payload_len0, flow0->info);
+ }
+
+enqueue0:
+ to_next[0] = bi0;
+ to_next++;
+ n_left_to_next--;
+ next0 = flow0->next_index;
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+
+trace0:
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dpi_rx_trace_t *tr
+ = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->app_id = flow0->info->app_id;
+ tr->next_index = next0;
+ tr->error = b0->error;
+ tr->flow_id = flow_index0;
+ }
+
+ from += 1;
+ n_left_from -= 1;
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (dpi4_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dpi_input_inline (vm, node, frame, /* is_ip4 */ 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dpi4_input_node) =
+{
+ .name = "dpi4-input",
+ .vector_size = sizeof (u32),
+ .n_errors = DPI_INPUT_N_ERROR,
+ .error_strings = dpi_input_error_strings,
+ .n_next_nodes = DPI_INPUT_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [DPI_INPUT_NEXT_##s] = n,
+ foreach_dpi_input_next
+#undef _
+ },
+ .format_trace = format_dpi_rx_trace,
+};
+
+/* *INDENT-ON* */
+
+/* Dummy init function to get us linked in. */
+static clib_error_t *
+dpi4_input_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (dpi4_input_init);
+
+
+VLIB_NODE_FN (dpi6_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dpi_input_inline (vm, node, frame, /* is_ip4 */ 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dpi6_input_node) =
+{
+ .name = "dpi6-input",
+ .vector_size = sizeof (u32),
+ .n_errors = DPI_INPUT_N_ERROR,
+ .error_strings = dpi_input_error_strings,
+ .n_next_nodes = DPI_INPUT_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [DPI_INPUT_NEXT_##s] = n,
+ foreach_dpi_input_next
+#undef _
+ },
+ .format_trace = format_dpi_rx_trace,
+};
+/* *INDENT-ON* */
+
+/* Dummy init function to get us linked in. */
+static clib_error_t *
+dpi6_input_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (dpi6_input_init);
+
+
+
+#define foreach_dpi_flow_input_next \
+_(DROP, "error-drop") \
+_(IP4_LOOKUP, "ip4-lookup")
+
+typedef enum
+{
+#define _(s,n) DPI_FLOW_NEXT_##s,
+ foreach_dpi_flow_input_next
+#undef _
+ DPI_FLOW_N_NEXT,
+} dpi_flow_input_next_t;
+
+#define foreach_dpi_flow_error \
+ _(NONE, "no error") \
+ _(IP_CHECKSUM_ERROR, "Rx ip checksum errors") \
+ _(IP_HEADER_ERROR, "Rx ip header errors") \
+ _(UDP_CHECKSUM_ERROR, "Rx udp checksum errors") \
+ _(UDP_LENGTH_ERROR, "Rx udp length errors")
+
+typedef enum
+{
+#define _(f,s) DPI_FLOW_ERROR_##f,
+ foreach_dpi_flow_error
+#undef _
+ DPI_FLOW_N_ERROR,
+} dpi_flow_error_t;
+
+static char *dpi_flow_error_strings[] = {
+#define _(n,s) s,
+ foreach_dpi_flow_error
+#undef _
+};
+
+static_always_inline u8
+dpi_check_ip4 (ip4_header_t * ip4, u16 payload_len)
+{
+ u16 ip_len = clib_net_to_host_u16 (ip4->length);
+ return ip_len > payload_len || ip4->ttl == 0
+ || ip4->ip_version_and_header_length != 0x45;
+}
+
+static_always_inline u8
+dpi_check_ip6 (ip6_header_t * ip6, u16 payload_len)
+{
+ u16 ip_len = clib_net_to_host_u16 (ip6->payload_length);
+ return ip_len > (payload_len - sizeof (ip6_header_t))
+ || ip6->hop_limit == 0
+ || (ip6->ip_version_traffic_class_and_flow_label >> 28) != 0x6;
+}
+
+always_inline uword
+dpi_flow_input_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, u32 is_ip4)
+{
+ dpi_main_t *dm = &dpi_main;
+ u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ u32 bi0, next0 = DPI_FLOW_NEXT_IP4_LOOKUP;
+ vlib_buffer_t *b0;
+ ip4_header_t *ip40;
+ ip6_header_t *ip60;
+ tcp_header_t *tcp0;
+ udp_header_t *udp0;
+ u32 flow_id0 = ~0;
+ u32 flow_index0 = ~0;
+ u32 is_reverse0 = 0;
+ dpi_flow_entry_t *flow0;
+ u32 ip_len0, l4_len0, payload_len0;
+ u8 protocol0;
+ u8 *l4_pkt0, *payload0;
+ u16 dst_port = 0;
+ segment *seg = 0;
+ segment *prev_seg = 0;
+
+ bi0 = to_next[0] = from[0];
+ b0 = vlib_get_buffer (vm, bi0);
+ ip_len0 = vlib_buffer_length_in_chain (vm, b0);
+
+ if (is_ip4)
+ {
+ ip40 = vlib_buffer_get_current (b0);
+ dpi_check_ip4 (ip40, ip_len0);
+ }
+ else
+ {
+ ip60 = vlib_buffer_get_current (b0);
+ dpi_check_ip6 (ip60, ip_len0);
+ }
+
+ ASSERT (b0->flow_id != 0);
+ flow_id0 = b0->flow_id - dm->flow_id_start;
+
+ is_reverse0 = (u32) ((flow_id0 >> 31) & 0x1);
+ flow_index0 = (u32) (flow_id0 & (u32) (~(1 << 31)));
+ flow0 = pool_elt_at_index (dm->dpi_flows, flow_index0);
+
+ /* have detected successfully, directly return */
+ if (flow0->info->detect_done)
+ goto enqueue0;
+
+ /* check layer4 */
+ if (is_ip4)
+ {
+ l4_pkt0 = (u8 *) (ip40 + 1);
+ l4_len0 = ip_len0 - sizeof (ip4_header_t);
+ protocol0 = ip40->protocol;
+ }
+ else
+ {
+ l4_pkt0 = (u8 *) (ip60 + 1);
+ l4_len0 = ip_len0 - sizeof (ip6_header_t);
+ protocol0 = ip60->protocol;
+ }
+
+ if ((protocol0 == IP_PROTOCOL_TCP) && (l4_len0 >= 20))
+ {
+ tcp0 = (tcp_header_t *) l4_pkt0;
+ payload_len0 = l4_len0 - tcp_doff (tcp0) * 4;
+ payload0 = l4_pkt0 + tcp_doff (tcp0) * 4;
+ dst_port = tcp0->dst_port;
+ }
+ else if ((protocol0 == IP_PROTOCOL_UDP) && (l4_len0 >= 8))
+ {
+ udp0 = (udp_header_t *) l4_pkt0;
+ payload_len0 = l4_len0 - sizeof (udp_header_t);
+ payload0 = l4_pkt0 + sizeof (udp_header_t);
+ dst_port = udp0->dst_port;
+ }
+ else
+ {
+ payload_len0 = l4_len0;
+ payload0 = l4_pkt0;
+ }
+
+ flow0->info->l4_protocol = protocol0;
+ flow0->info->dst_port = dst_port;
+
+ /* TCP stream reassembly and detect a protocol pdu */
+ if ((protocol0 == IP_PROTOCOL_TCP) && (flow0->reass_en))
+ {
+ dpi_handle_tcp_stream (flow0, bi0, l4_pkt0, payload_len0,
+ is_reverse0);
+
+ /* This packet has been consumed, retrieve next packet */
+ if (flow0->consumed)
+ goto trace0;
+
+ /* send out continuous scanned segments */
+ seg = flow0->first_seg;
+ dpi_enqueue_tcp_segments (seg, vm, node, next_index, to_next,
+ n_left_to_next, bi0, next0);
+ flow0->first_seg = 0;
+
+ /* Here detected successfully, send out remaining segments in seg_queue */
+ if (flow0->info->detect_done)
+ {
+ seg = flow0->c2s.seg_queue;
+ dpi_enqueue_tcp_segments (seg, vm, node, next_index,
+ to_next, n_left_to_next, bi0,
+ next0);
+ flow0->c2s.seg_queue = 0;
+
+ seg = flow0->s2c.seg_queue;
+ dpi_enqueue_tcp_segments (seg, vm, node, next_index,
+ to_next, n_left_to_next, bi0,
+ next0);
+ flow0->s2c.seg_queue = 0;
+ }
+ goto trace0;
+ }
+ else
+ {
+ /* detect layer 7 application for single packet */
+ dpi_detect_application (payload0, payload_len0, flow0->info);
+ }
+
+enqueue0:
+ to_next[0] = bi0;
+ to_next++;
+ n_left_to_next--;
+ next0 = flow0->next_index;
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+
+trace0:
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ dpi_rx_trace_t *tr
+ = vlib_add_trace (vm, node, b0, sizeof (*tr));
+ tr->app_id = flow0->info->app_id;
+ tr->next_index = next0;
+ tr->error = b0->error;
+ tr->flow_id = flow_index0;
+ }
+
+ from += 1;
+ n_left_from -= 1;
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+
+VLIB_NODE_FN (dpi4_flow_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dpi_flow_input_inline (vm, node, frame, /* is_ip4 */ 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dpi4_flow_input_node) = {
+ .name = "dpi4-flow-input",
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .vector_size = sizeof (u32),
+
+ .format_trace = format_dpi_rx_trace,
+
+ .n_errors = DPI_FLOW_N_ERROR,
+ .error_strings = dpi_flow_error_strings,
+
+ .n_next_nodes = DPI_FLOW_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [DPI_FLOW_NEXT_##s] = n,
+ foreach_dpi_flow_input_next
+#undef _
+ },
+};
+/* *INDENT-ON* */
+
+/* Dummy init function to get us linked in. */
+static clib_error_t *
+dpi4_flow_input_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (dpi4_flow_input_init);
+
+VLIB_NODE_FN (dpi6_flow_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return dpi_flow_input_inline (vm, node, frame, /* is_ip4 */ 0);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dpi6_flow_input_node) = {
+ .name = "dpi6-flow-input",
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .vector_size = sizeof (u32),
+
+ .format_trace = format_dpi_rx_trace,
+
+ .n_errors = DPI_FLOW_N_ERROR,
+ .error_strings = dpi_flow_error_strings,
+
+ .n_next_nodes = DPI_FLOW_N_NEXT,
+ .next_nodes = {
+#define _(s,n) [DPI_FLOW_NEXT_##s] = n,
+ foreach_dpi_flow_input_next
+#undef _
+ },
+};
+/* *INDENT-ON* */
+
+/* Dummy init function to get us linked in. */
+static clib_error_t *
+dpi6_flow_input_init (vlib_main_t * vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (dpi6_flow_input_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/dpi_plugin_doc.md b/src/dpi_plugin_doc.md
new file mode 100644
index 0000000..fc069c0
--- /dev/null
+++ b/src/dpi_plugin_doc.md
@@ -0,0 +1,107 @@
+# DPI plugin for VPP {#dpi_plugin_doc}
+
+## Overview
+
+DPI plugin can identify and analyze the traffic running on networks in real time.
+It can be used on many use cases, such as Web Application Firewall,
+Policy based routing, Intrusion Detection System, Intrusion Prevention System, etc.
+
+The main use case for current approach would be identification of cooperating traffic
+for an established TCP connection (i.e. traffic that is not intentionally disguised)
+to support application-based QoS.
+
+
+## Design
+
+The DPI plugin leverage Hyperscan to perform regex matching.
+
+Hyperscan is a high-performance multiple regex matching library.
+Please refer to below for details:
+http://intel.github.io/dpi/dev-reference/
+
+Below is the brief design:
+
+1. Provides a default APPID database for detection.
+
+2. Support TCP connection state tracking.
+
+3. Support TCP segments reassembly on the fly, which handles out-of-order tcp segments and overlapping segments.
+ It means that we do not need to reassembly segments first, then dedect applicaion,
+ and then fragment segments again, which helps to improve performance.
+
+4. Support Hyperscan Stream mode, which can detect one rule straddling into some tcp segments.
+ It means that if there is a rule "abcde", then "abc" can be in packet 1,
+ and "de" can be in packet 2.
+
+5. Configure static dpi flows with 5-tuple and VRF-aware, and supports both ipv4 and ipv6 flows.
+ These flows will first try to HW offload to NIC based on DPDK rte_flow mechanism
+ and vpp/vnet/flow infrastructure.
+ If failed, then will create static SW flow mappings.
+ Each flow configuration will create two HW or SW flow mappings, i.e. for forward and reverse traffic.
+ And both flow mappings will be mapped to the same dpi flow.
+ Dynamically create new SW mapping and aging out mechanism will be added later.
+
+ "dpi flow [add | del] "
+ "[src-ip <ip-addr>] [dst-ip <ip-addr>] "
+ "[src-port <port>] [dst-port <port>] "
+ "[protocol <protocol>] [vrf-id <nn>]",
+
+ "dpi tcp reass flow_id <nn> <enable|disable> "
+ "[ <client | server | both> ]",
+
+ "dpi set flow-offload hw <interface-name> rx <flow-id> [del]",
+
+ "dpi set ip4 flow-bypass <interface> [del]",
+
+6. When HW flow offload matched, packets will be redirected to DPI plugin with dpi flow_id in packet descriptor.
+ If not, packets will be bypassed to DPI plugin from ip-input, and then lookup SW flow mapping table.
+
+7. Then will detect layer 7 applications.
+ This first patch only detect sub protocls within SSL/TLS.
+ 1). Identify SSL/TLS certificate message and subsequent segments.
+ 2). Scan SSL/TLS certificate message through hyperscan, and get application id if matched.
+ 3). If maximum packets for this flow are checked and not found matched application, the detection will end up.
+
+
+## Hyperscan Installation
+
+Hyperscan can be installed from packages directly on below OS:
+ Ubuntu 16.04.03
+ Ubuntu 18.04 and later version
+ Fedora 27 and later version
+ openSUSE rolling-release Tumbleweed and later version
+
+If you cannot install Hyperscan from packages directly,
+you can build and install it from the source code.
+
+Below are steps to build and install Hyperscan on Ubuntu 16.04:
+1).Install binary prerequisites
+apt-get install cmake ragel
+apt-get install libboost-dev
+apt-get install python-dev libbz2-dev
+
+2).Download Hyperscan sources
+wget https://github.com/intel/hyperscan/archive/v5.0.0.tar.gz
+tar -xf v5.0.0.tar.gz
+
+3).Download boost headers
+wget https://dl.bintray.com/boostorg/release/1.68.0/source/boost_1_68_0.tar.gz
+tar -xf boost_1_68_0.tar.gz
+cp -r boost_1_68_0/boost hyperscan-5.0.0/include
+
+4).Build and install Hyperscan shared library.
+ Just follow the instruction from here. Compilation can take a long time.
+cd hyperscan-5.0.0
+mkdir build
+cd build
+cmake -DBUILD_SHARED_LIBS=true ..
+make
+make install
+
+## Multi-Thread Support
+Since generated bytecode database is read only, you can run multiple cores
+to utilize the byte database to scale.
+
+
+
+
diff --git a/src/protocols/dpi_ssl.c b/src/protocols/dpi_ssl.c
new file mode 100644
index 0000000..f15a19c
--- /dev/null
+++ b/src/protocols/dpi_ssl.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2019 Intel and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+
+#include "../dpi.h"
+
+typedef enum
+{
+ State_Initial = 0,
+ State_Client_Hello = 1,
+ State_Server_Hello = 2,
+ State_Certificate = 3,
+} ssl_state;
+
+enum
+{
+ MAJOR_TLS = 0x3,
+};
+
+enum
+{
+ MINOR_SSL30 = 0,
+ MINOR_TLS10 = 0x1,
+ MINOR_TLS11 = 0x2,
+ MINOR_TLS12 = 0x3,
+};
+
+typedef enum
+{
+ change_cipher_spec = 20,
+ alert = 21,
+ handshake = 22,
+ application_data = 23,
+} ContentType;
+
+typedef struct
+{
+ u8 major;
+ u8 minor;
+} ProtocolVersion;
+
+typedef struct
+{
+ u8 type;
+ ProtocolVersion version;
+ u16 length;
+} __attribute__ ((packed)) ssl_header;
+
+typedef enum
+{
+ hello_request = 0,
+ client_hello = 1,
+ server_hello = 2,
+ certificate = 11,
+ server_key_exchange = 12,
+ certificate_request = 13,
+ server_hello_done = 14,
+ certificate_verify = 15,
+ client_key_exchange = 16,
+ finished = 20,
+} HandshakeType;
+
+typedef struct
+{
+ u8 msg_type; /* handshake type */
+ u8 length[3]; /* bytes in message */
+} Handshake_header;
+
+int dpi_ssl_detect_protocol_from_cert (u8 * payload, u32 payload_len,
+ dpi_flow_info_t * flow);
+
+#define dpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e)
+#define dpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
+#define dpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9')
+#define dpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' '))
+#define dpi_min(a,b) ((a < b) ? a : b)
+
+static void
+dpi_set_detected_protocol (dpi_flow_info_t * flow,
+ u32 upper_protocol, u32 lower_protocol)
+{
+
+ if ((upper_protocol == DPI_PROTOCOL_UNKNOWN)
+ && (lower_protocol != DPI_PROTOCOL_UNKNOWN))
+ upper_protocol = lower_protocol;
+
+ if (upper_protocol == lower_protocol)
+ lower_protocol = DPI_PROTOCOL_UNKNOWN;
+
+ if ((upper_protocol != DPI_PROTOCOL_UNKNOWN)
+ && (lower_protocol == DPI_PROTOCOL_UNKNOWN))
+ {
+ if ((flow->guessed_host_protocol_id != DPI_PROTOCOL_UNKNOWN)
+ && (upper_protocol != flow->guessed_host_protocol_id))
+ {
+ lower_protocol = upper_protocol;
+ upper_protocol = flow->guessed_host_protocol_id;
+ }
+ }
+
+ flow->detected_protocol[0] = upper_protocol;
+ flow->detected_protocol[1] = lower_protocol;
+}
+
+static u32
+dpi_ssl_refine_master_protocol (dpi_flow_info_t * flow, u32 protocol)
+{
+
+ if (flow->l4.tcp.ssl_got_server_cert == 1)
+ protocol = DPI_PROTOCOL_SSL;
+ else
+ protocol = DPI_PROTOCOL_SSL_NO_CERT;
+
+ return protocol;
+}
+
+int
+dpi_ssl_detect_protocol_from_cert (u8 * payload, u32 payload_len,
+ dpi_flow_info_t * flow)
+{
+ u32 host_protocol = DPI_PROTOCOL_UNKNOWN;
+ int rv = 0;
+
+ /* Only check SSL handshake packets.
+ * Check first segment and subsequent segments. */
+ if (((payload_len > (sizeof (ssl_header) + sizeof (Handshake_header)))
+ && (payload[0] == handshake)) || (flow->detect_begin))
+ {
+ if ((flow->detected_protocol[0] == DPI_PROTOCOL_UNKNOWN)
+ || (flow->detected_protocol[0] == DPI_PROTOCOL_SSL))
+ {
+ rv = dpi_search_host_protocol (flow, (char *) payload, payload_len,
+ DPI_PROTOCOL_SSL, &host_protocol);
+
+ if (host_protocol != DPI_PROTOCOL_UNKNOWN)
+ {
+ dpi_set_detected_protocol (flow, host_protocol,
+ dpi_ssl_refine_master_protocol (flow,
+ DPI_PROTOCOL_SSL));
+ return rv;
+ }
+ }
+ }
+ return 0;
+}
+
+
+void
+dpi_search_tcp_ssl (u8 * payload, u32 payload_len, dpi_flow_info_t * flow)
+{
+ u32 cur_len = payload_len;
+ u32 cur_len2;
+ u8 handshake_type;
+
+ /* Check first segment of SSL Certificate message */
+ if ((payload_len > (sizeof (ssl_header) + sizeof (Handshake_header)))
+ && (payload[0] == handshake))
+ {
+ handshake_type = payload[5];
+
+ if (handshake_type == client_hello)
+ {
+ flow->l4.tcp.ssl_stage = State_Client_Hello;
+ return;
+ }
+ else if (handshake_type == server_hello)
+ {
+ cur_len = ntohs (get_u16_t (payload, 3)) + sizeof (ssl_header);
+
+ /* This packet only contains Server Hello message */
+ if (cur_len == payload_len)
+ {
+ flow->l4.tcp.ssl_stage = State_Server_Hello;
+ return;
+ }
+
+ /* This packet contains Server Hello, Certificate and more messages */
+ if (payload_len >= cur_len + sizeof (ssl_header)
+ && payload[cur_len] == handshake
+ && payload[cur_len + 1] == MAJOR_TLS)
+ {
+ cur_len2 = ntohs (get_u16_t (payload, cur_len + 3))
+ + sizeof (ssl_header);
+ if (payload[cur_len + 5] == certificate)
+ {
+ flow->l4.tcp.ssl_stage = State_Certificate;
+ flow->detect_begin = 1;
+ /* Scan segments of certificate message */
+ if (dpi_ssl_detect_protocol_from_cert (&payload[cur_len],
+ cur_len2, flow) > 0)
+ return;
+ }
+ }
+ }
+ else if (handshake_type == certificate)
+ {
+ cur_len = ntohs (get_u16_t (payload, 3)) + sizeof (ssl_header);
+
+ /* This packet contains first segment of certificate message */
+ if (cur_len == payload_len)
+ {
+ flow->l4.tcp.ssl_stage = State_Certificate;
+ flow->detect_begin = 1;
+ /* Scan segments of certificate message */
+ if (dpi_ssl_detect_protocol_from_cert (payload, cur_len, flow) >
+ 0)
+ return;
+ }
+ }
+ else if (flow->detect_begin)
+ {
+ /* Check subsequent segments of SSL Certificate message */
+ if (dpi_ssl_detect_protocol_from_cert (payload, cur_len, flow) > 0)
+ return;
+ }
+ }
+
+ return;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */