aboutsummaryrefslogtreecommitdiffstats
path: root/src/dpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dpi.c')
-rw-r--r--src/dpi.c723
1 files changed, 723 insertions, 0 deletions
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:
+ */