aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt44
-rw-r--r--src/dpi.api42
-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.c1042
-rw-r--r--src/dpi_plugin_doc.md107
-rw-r--r--src/protocols/dpi_ssl.c247
10 files changed, 3176 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..5eb40d3
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (c) 2018 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.
+
+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})
+ add_vpp_plugin(dpi
+ SOURCES
+ dpi.c
+ dpi_api.c
+ dpi_cli.c
+ dpi_node.c
+ protocols/dpi_ssl.c
+
+ API_FILES
+ dpi.api
+
+ INSTALL_HEADERS
+ dpi_app_match.h
+ dpi.h
+
+ LINK_LIBRARIES
+ ${HYPERSCAN_LIB}
+ )
+ message(STATUS "Found Hyperscan in ${HYPERSCAN_INCLUDE_DIR}")
+else()
+ message(WARNING "-- Hyperscan not found - dpi_plugin disabled")
+endif()
+
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.c b/src/dpi.c
new file mode 100644
index 0000000..1cb482e
--- /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/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..540d1a2
--- /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/dpi.h>
+
+
+#define vl_msg_id(n,h) n,
+typedef enum
+{
+#include <dpi/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/dpi.api.h>
+#undef vl_typedefs
+
+/* define generated endian-swappers */
+#define vl_endianfun
+#include <dpi/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/dpi.api.h>
+#undef vl_printfun
+
+/* Get the API version number */
+#define vl_api_version(n,v) static u32 api_version=(v);
+#include <dpi/dpi.api.h>
+#undef vl_api_version
+
+#define vl_msg_name_crc_list
+#include <dpi/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..6b3afc7
--- /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/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..2e86c65
--- /dev/null
+++ b/src/dpi_node.c
@@ -0,0 +1,1042 @@
+/*
+ * 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/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;
+ int rv;
+
+ 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]);
+ rv =
+ 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]);
+ rv =
+ parse_ip6_packet_and_lookup(ip60, fib_index0, &key60,
+ &not_found0, &flow_id0);
+ }
+
+ if (!rv)
+ goto enqueue0;
+
+ 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..7257ca5
--- /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/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:
+ */