summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author刘学利 <[email protected]>2021-04-27 09:49:15 +0000
committer刘学利 <[email protected]>2021-04-27 09:49:15 +0000
commitd6393940779a53352c761db9102e817e3b3e444d (patch)
treeec7710f49cd24cfb5c2a928d9e8fcf19163039a7
parent1a4d35dec8f7304a2efa677cd5684f581e4d0bf2 (diff)
支持同步接收APP的识别结果v4.0.0
支持一个流具有多个APP属性 适配新的APP ID
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--bin/app_l7_proto_id.conf3
-rw-r--r--bin/main.conf1
-rw-r--r--bin/tsg_l7_protocol.conf55
-rw-r--r--inc/app_label.h32
-rw-r--r--inc/tsg_send_log.h1
-rw-r--r--src/tsg_entry.cpp1173
-rw-r--r--src/tsg_entry.h55
-rw-r--r--src/tsg_rule.cpp263
-rw-r--r--src/tsg_send_log.cpp740
-rw-r--r--src/tsg_send_log_internal.h3
-rw-r--r--src/uthash.h1150
12 files changed, 2326 insertions, 1152 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ed470f3..36a9342 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,7 +3,7 @@ variables:
GIT_STRATEGY: "clone"
BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/
INSTALL_PREFIX: "/home/mesasoft/sapp_run/"
- INSTALL_DEPENDENCY_LIBRARY: libMESA_handle_logger-devel libcjson-devel libMESA_field_stat2-devel sapp sapp-devel framework_env libMESA_prof_load-devel http-devel dns-devel ftp-devel mail-devel ssl-devel librdkafka-devel libmaatframe-devel quic-devel libasan
+ INSTALL_DEPENDENCY_LIBRARY: libMESA_handle_logger-devel libcjson-devel libMESA_field_stat2-devel sapp sapp-devel framework_env libMESA_prof_load-devel http-devel dns-devel ftp-devel mail-devel ssl-devel librdkafka-devel libmaatframe-devel quic-devel mesa_sip-devel libasan
stages:
- build
diff --git a/bin/app_l7_proto_id.conf b/bin/app_l7_proto_id.conf
index 714f943..8c48b31 100644
--- a/bin/app_l7_proto_id.conf
+++ b/bin/app_l7_proto_id.conf
@@ -49,3 +49,6 @@ STRING BJNP 147
STRING LDAP 148
STRING RTMP 149
STRING RTSP 150
+STRING POP3 116
+STRING SMTP 122
+STRING IMAP 151
diff --git a/bin/main.conf b/bin/main.conf
index 5227583..2e9e042 100644
--- a/bin/main.conf
+++ b/bin/main.conf
@@ -31,4 +31,5 @@ ENTRANCE_ID=18
LOG_LEVEL=10
LOG_PATH=./tsglog/tsg_master
POLICY_PRIORITY_LABEL=POLICY_PRIORITY
+L7_RPTOCOL_FILE="./tsgconf/tsg_l7_protocol.conf"
DEVICE_ID_COMMAND=hostname | awk -F'-' '{print $3}'| awk -F'ADC' '{print $2}' \ No newline at end of file
diff --git a/bin/tsg_l7_protocol.conf b/bin/tsg_l7_protocol.conf
new file mode 100644
index 0000000..9c287c1
--- /dev/null
+++ b/bin/tsg_l7_protocol.conf
@@ -0,0 +1,55 @@
+#TYPE:1:UCHAR,2:USHORT,3:USTRING,4:ULOG,5:USTRING,6:FILE,7:UBASE64,8:PACKET
+#TYPE FIELD VALUE
+STRING UNCATEGORIZED 15001
+STRING UNCATEGORIZED 15002
+STRING UNKNOWN_OTHER 15003
+STRING DNS 32
+STRING FTP 45
+STRING FTPS 751
+STRING HTTP 67
+STRING HTTPS 68
+STRING ICMP 70
+STRING IKE 15004
+STRING MAIL 15005
+STRING IMAP 75
+STRING IMAPS 76
+STRING IPSEC 85
+STRING XMPP 94
+STRING L2TP 98
+STRING NTP 137
+STRING POP3 147
+STRING POP3S 148
+STRING PPTP 153
+STRING QUIC 2521
+STRING SIP 182
+STRING SMB 185
+STRING SMTP 186
+STRING SMTPS 187
+STRING SPDY 1469
+STRING SSH 198
+STRING SSL 199
+STRING SOCKS 15006
+STRING TELNET 209
+STRING DHCP 29
+STRING RADIUS 158
+STRING OPENVPN 336
+STRING STUN 201
+STRING TEREDO 555
+STRING DTLS 1291
+STRING DoH 15007
+STRING ISAKMP 92
+STRING MDNS 3835
+STRING NETBIOS 129
+STRING NETFLOW 130
+STRING RDP 150
+STRING RTCP 174
+STRING RTP 175
+STRING SLP 15008
+STRING SNMP 190
+STRING SSDP 197
+STRING TFTP 211
+STRING BJNP 2481
+STRING LDAP 100
+STRING RTMP 337
+STRING RTSP 176
+STRING ESNI 15009
diff --git a/inc/app_label.h b/inc/app_label.h
index c6b9bd3..4d073ad 100644
--- a/inc/app_label.h
+++ b/inc/app_label.h
@@ -1,17 +1,39 @@
#ifndef __APP_LABEL_H__
#define __APP_LABEL_H__
+#define MAX_APP_ID_NUM 8
+
struct app_id_label
{
- int surrogate_id;
- int app_id;
+ int app_id_num;
+ unsigned int app_id[MAX_APP_ID_NUM];
+ unsigned int surrogate_id[MAX_APP_ID_NUM];
};
struct basic_proto_label
{
- unsigned char continue_scan_flag; //0: stop; 1: continue
- unsigned char pad;
- unsigned short proto_id;
+ int continue_scan_flag; //0: stop; 1: continue
+ int protocol_id_num;
+ unsigned short protocol_id[MAX_APP_ID_NUM];
+};
+
+
+enum APP_IDENTIFY_ORIGIN
+{
+ ORIGIN_BASIC_PROTOCOL,
+ ORIGIN_USER_DEFINE,
+ ORIGIN_DKPT,
+ ORIGIN_QM_ENGINE,
+ ORIGIN_MAX
+};
+
+
+struct app_identify_result
+{
+ enum APP_IDENTIFY_ORIGIN origin;
+ int app_id_num;
+ unsigned int app_id[MAX_APP_ID_NUM];
+ unsigned int surrogate_id[MAX_APP_ID_NUM];
};
enum _ATTRIBUTE_TYPE
diff --git a/inc/tsg_send_log.h b/inc/tsg_send_log.h
index 6c76568..f0c6dc8 100644
--- a/inc/tsg_send_log.h
+++ b/inc/tsg_send_log.h
@@ -36,6 +36,5 @@ int TLD_cancel(struct TLD_handle_t *handle);
int tsg_send_log(struct tsg_log_instance_t *instance, struct TLD_handle_t *handle, tsg_log_t *log_msg, int thread_id);
unsigned long long tsg_get_stream_id(struct streaminfo *a_stream);
-char *tsg_l7_protocol_id2name(struct tsg_log_instance_t *instance, unsigned short id);
#endif
diff --git a/src/tsg_entry.cpp b/src/tsg_entry.cpp
index e54477f..e788146 100644
--- a/src/tsg_entry.cpp
+++ b/src/tsg_entry.cpp
@@ -9,7 +9,8 @@
#include <MESA/ftp.h>
#include <MESA/ssl.h>
#include <MESA/mail.h>
-#include "MESA/gquic.h"
+#include <MESA/gquic.h>
+#include "MESA/mesa_sip.h"
#include <MESA/stream.h>
#include <MESA/MESA_prof_load.h>
#include <MESA/MESA_handle_logger.h>
@@ -49,43 +50,43 @@ char TSG_MASTER_VERSION_20200805=0;
const char *tsg_conffile="tsgconf/main.conf";
g_tsg_para_t g_tsg_para;
-id2field_t g_tsg_fs2_field[TSG_FS2_MAX]={{TLD_TYPE_UNKNOWN, TSG_FS2_TCP_LINKS, "tcp_links"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_UDP_LINKS, "udp_links"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_BYPASS, "bypass"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_HIT_ADDR, "hit_addr"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_HIT_SHARE, "hit_share"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_INTERCEPT, "intercept"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_EXCLUSION, "exclusion"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_SUCCESS_LOG, "success_log"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_FAILED_LOG, "failed_log"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_DROP_LOG, "drop_log"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_ABORT_ALLOW, "abort_allow"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_ABORT_DENY, "abort_deny"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_ABORT_MONITOR, "abort_monitor"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_ABORT_INTERCEPT, "abort_intercept"},
- {TLD_TYPE_UNKNOWN, TSG_FS2_ABORT_UNKNOWN, "abort_unknown"}
+id2field_t g_tsg_fs2_field[TSG_FS2_MAX]={{0, TSG_FS2_TCP_LINKS, "tcp_links"},
+ {0, TSG_FS2_UDP_LINKS, "udp_links"},
+ {0, TSG_FS2_BYPASS, "bypass"},
+ {0, TSG_FS2_HIT_ADDR, "hit_addr"},
+ {0, TSG_FS2_HIT_SHARE, "hit_share"},
+ {0, TSG_FS2_INTERCEPT, "intercept"},
+ {0, TSG_FS2_EXCLUSION, "exclusion"},
+ {0, TSG_FS2_SUCCESS_LOG, "success_log"},
+ {0, TSG_FS2_FAILED_LOG, "failed_log"},
+ {0, TSG_FS2_DROP_LOG, "drop_log"},
+ {0, TSG_FS2_ABORT_ALLOW, "abort_allow"},
+ {0, TSG_FS2_ABORT_DENY, "abort_deny"},
+ {0, TSG_FS2_ABORT_MONITOR, "abort_monitor"},
+ {0, TSG_FS2_ABORT_INTERCEPT, "abort_intercept"},
+ {0, TSG_FS2_ABORT_UNKNOWN, "abort_unknown"}
};
-id2field_t g_tsg_proto_name2id[PROTO_MAX]={{TLD_TYPE_UNKNOWN, PROTO_UNKONWN, "unknown"},
- {TLD_TYPE_UNKNOWN, PROTO_IPv4, "IPV4"},
- {TLD_TYPE_UNKNOWN, PROTO_IPv6, "IPV6"},
- {TLD_TYPE_UNKNOWN, PROTO_TCP, "TCP"},
- {TLD_TYPE_UNKNOWN, PROTO_UDP, "UDP"},
- {TLD_TYPE_UNKNOWN, PROTO_HTTP, "HTTP"},
- {TLD_TYPE_UNKNOWN, PROTO_MAIL, "MAIL"},
- {TLD_TYPE_UNKNOWN, PROTO_DNS, "DNS"},
- {TLD_TYPE_UNKNOWN, PROTO_FTP, "FTP"},
- {TLD_TYPE_UNKNOWN, PROTO_SSL, "SSL"},
- {TLD_TYPE_UNKNOWN, PROTO_SIP, "SIP"},
- {TLD_TYPE_UNKNOWN, PROTO_BGP, "BGP"},
- {TLD_TYPE_UNKNOWN, PROTO_STREAMING_MEDIA, "STREAMING_MEDIA"},
- {TLD_TYPE_UNKNOWN, PROTO_QUIC, "QUIC"},
- {TLD_TYPE_UNKNOWN, PROTO_SSH, "SSH"},
- {TLD_TYPE_UNKNOWN, PROTO_SMTP, "SMTP"},
- {TLD_TYPE_UNKNOWN, PROTO_IMAP, "IMAP"},
- {TLD_TYPE_UNKNOWN, PROTO_POP3, "POP3"},
- {TLD_TYPE_UNKNOWN, PROTO_RTP, "RTP"},
- {TLD_TYPE_UNKNOWN, PROTO_APP, "APP"}
+id2field_t g_tsg_proto_name2id[PROTO_MAX]={{PROTO_UNKONWN, 0, "unknown"},
+ {PROTO_IPv4, 0, "IPV4"},
+ {PROTO_IPv6, 0, "IPV6"},
+ {PROTO_TCP, 0, "TCP"},
+ {PROTO_UDP, 0, "UDP"},
+ {PROTO_HTTP, 0, "HTTP"},
+ {PROTO_MAIL, 0, "MAIL"},
+ {PROTO_DNS, 0, "DNS"},
+ {PROTO_FTP, 0, "FTP"},
+ {PROTO_SSL, 0, "SSL"},
+ {PROTO_SIP, 0, "SIP"},
+ {PROTO_BGP, 0, "BGP"},
+ {PROTO_STREAMING_MEDIA, 0, "STREAMING_MEDIA"},
+ {PROTO_QUIC, 0, "QUIC"},
+ {PROTO_SSH, 0, "SSH"},
+ {PROTO_SMTP, 0, "SMTP"},
+ {PROTO_IMAP, 0, "IMAP"},
+ {PROTO_POP3, 0, "POP3"},
+ {PROTO_RTP, 0, "RTP"},
+ {PROTO_APP, 0,"APP"}
};
#define DECCRYPTION_EXCLUSION_ALLOW_POLICY_ID 1
@@ -94,14 +95,11 @@ id2field_t g_tsg_proto_name2id[PROTO_MAX]={{TLD_TYPE_UNKNOWN, PROTO_UNKONWN, "un
static int init_context(void **pme, int thread_seq)
{
struct master_context *context=(struct master_context *)*pme;
-
+
*pme=dictator_malloc(thread_seq, sizeof(struct master_context));
memset(*pme, 0, sizeof(struct master_context));
context=(struct master_context *)*pme;
- context->domain_len=0;
- memset(context->domain, 0, sizeof(context->domain));
- context->continue_scan_app_id=APP_SCAN_FLAG_CONTINUE;
context->continue_scan_proto_id=APP_SCAN_FLAG_CONTINUE;
return 0;
@@ -155,26 +153,6 @@ static int get_device_id(char *command, int entrance_id)
return (entrance_id<<7)+(atoi(buffer)%128);
}
-static int is_repetitive_protocol_id(unsigned short proto_id)
-{
- switch(proto_id)
- {
- case DNS_PROTO_ID:
- case FTP_PROTO_ID:
- case HTTP_PROTO_ID:
- case MAIL_PROTO_ID:
- case QUIC_PROTO_ID:
- case SIP_PROTO_ID:
- case SSL_PROTO_ID:
- case RTP_PROTO_ID:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
static int is_only_monitor(struct Maat_rule_t *result, int hit_cnt)
{
int i=0;
@@ -190,84 +168,91 @@ static int is_only_monitor(struct Maat_rule_t *result, int hit_cnt)
return 1;
}
-static int get_default_policy(int compile_id, struct Maat_rule_t *result)
+static int set_drop_stream(const struct streaminfo *a_stream)
{
- struct Maat_rule_t p_result={0};
- struct compile_user_region *user_region=NULL;
-
- p_result.config_id=compile_id;
- user_region=(struct compile_user_region *)Maat_rule_get_ex_data(g_tsg_maat_feather, &p_result, g_tsg_para.table_id[TABLE_SECURITY_COMPILE]);
- if(user_region!=NULL)
- {
- if(user_region->result!=NULL)
- {
- memcpy(result, user_region->result, sizeof(struct Maat_rule_t));
- if(result->action==TSG_ACTION_BYPASS)
- {
- result->action=TSG_ACTION_NONE;
- }
- }
-
- security_compile_free(g_tsg_para.table_id[TABLE_SECURITY_COMPILE], &p_result, NULL, (MAAT_RULE_EX_DATA *)&user_region, 0, NULL);
- return 1;
- }
+ int opt_value=1;
+ MESA_set_stream_opt(a_stream, MSO_DROP_STREAM, (void *)&opt_value, sizeof(opt_value));
+ MESA_set_stream_opt(a_stream, MSO_TIMEOUT, (void *)&g_tsg_para.timeout, sizeof(g_tsg_para.timeout));
return 0;
+
}
-static struct app_id_label *get_app_id_label(struct streaminfo *a_stream, struct master_context *context, int thread_seq)
+static int set_struct_project(const struct streaminfo *a_stream, int project_id, void *data)
{
- struct app_id_label *app_id_label=NULL;
+ if(a_stream==NULL || project_id<0 || data==NULL)
+ {
+ return 0;
+ }
- if(context->continue_scan_app_id==APP_SCAN_FLAG_CONTINUE && g_tsg_para.app_id_project_id>=0)
- {
- app_id_label=(struct app_id_label *)project_req_get_struct(a_stream, g_tsg_para.app_id_project_id);
- if(app_id_label==NULL)
- {
- return NULL;
- }
-
- if(context->app_id!=app_id_label->app_id)
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "READ_APP_ID_FLAG",
- "Get app id label, app_id: %d addr: %s",
- app_id_label->app_id,
+ int ret=project_req_add_struct((struct streaminfo *)a_stream, project_id, data);
+ if(ret<0)
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger,
+ RLOG_LV_FATAL,
+ "PROJECT",
+ "Add project failed, project_id: %d addr: %s",
+ project_id,
PRINTADDR(a_stream, g_tsg_para.level)
);
- context->app_id=app_id_label->app_id;
- return app_id_label;
- }
+ return 0;
+ }
-
+ return 1;
+}
+
+static const void *get_struct_project(const struct streaminfo *a_stream, int project_id)
+{
+ if(a_stream==NULL || project_id<0)
+ {
+ return NULL;
}
- return NULL;
+ return project_req_get_struct(a_stream, project_id);
+}
+static int get_table_id(tsg_protocol_t protocol)
+{
+ switch(protocol)
+ {
+ case PROTO_HTTP:
+ return g_tsg_para.table_id[TABLE_HTTP_HOST];
+ case PROTO_SSL:
+ return g_tsg_para.table_id[TABLE_SSL_SNI];
+ case PROTO_QUIC:
+ return g_tsg_para.table_id[TABLE_QUIC_SNI];
+ default:
+ break;
+ }
+
+ return -1;
}
-static int get_basic_proto_id(struct streaminfo *a_stream, struct master_context *context, int thread_seq)
+static int get_default_policy(int compile_id, struct Maat_rule_t *result)
{
- struct basic_proto_label *proto_label=NULL;
+ struct Maat_rule_t p_result={0};
+ struct compile_user_region *user_region=NULL;
- if(context->continue_scan_proto_id==APP_SCAN_FLAG_CONTINUE && g_tsg_para.l7_proto_project_id>=0)
+ p_result.config_id=compile_id;
+ user_region=(struct compile_user_region *)Maat_rule_get_ex_data(g_tsg_maat_feather, &p_result, g_tsg_para.table_id[TABLE_SECURITY_COMPILE]);
+ if(user_region!=NULL)
{
- proto_label=(struct basic_proto_label *)project_req_get_struct(a_stream, g_tsg_para.l7_proto_project_id);
- if(proto_label!=NULL)
+ if(user_region->result!=NULL)
{
- if(proto_label->continue_scan_flag==APP_SCAN_FLAG_STOP)
+ memcpy(result, user_region->result, sizeof(struct Maat_rule_t));
+ if(result->action==TSG_ACTION_BYPASS)
{
- context->continue_scan_proto_id=APP_SCAN_FLAG_STOP;
+ result->action=TSG_ACTION_NONE;
}
-
- return proto_label->proto_id;
}
+
+ security_compile_free(g_tsg_para.table_id[TABLE_SECURITY_COMPILE], &p_result, NULL, (MAAT_RULE_EX_DATA *)&user_region, 0, NULL);
+ return 1;
}
return 0;
}
-static int master_send_log(struct streaminfo *a_stream, struct Maat_rule_t *p_result, int result_num, struct identify_info *identify_info, int thread_seq)
+static int master_send_log(const struct streaminfo *a_stream, struct Maat_rule_t *p_result, int result_num, char *domain, tsg_protocol_t proto, int thread_seq)
{
tsg_log_t log_msg;
char *domain_field_name=NULL;
@@ -275,47 +260,50 @@ static int master_send_log(struct streaminfo *a_stream, struct Maat_rule_t *p_re
struct TLD_handle_t *TLD_handle=NULL;
TLD_handle=TLD_create(thread_seq);
- if(identify_info!=NULL && (identify_info->proto>PROTO_UNKONWN) && (identify_info->proto<PROTO_MAX))
+ if(proto>PROTO_UNKONWN && proto<PROTO_MAX)
{
schema_field_name=log_field_id2name(g_tsg_log_instance, LOG_COMMON_SCHAME_TYPE);
- if(identify_info->proto==PROTO_IMAP || identify_info->proto==PROTO_SMTP || identify_info->proto==PROTO_POP3)
+ if(proto==PROTO_IMAP || proto==PROTO_SMTP || proto==PROTO_POP3)
{
TLD_append(TLD_handle, schema_field_name, (void *)g_tsg_proto_name2id[PROTO_MAIL].name, TLD_TYPE_STRING);
- TLD_append(TLD_handle, (char *)"mail_protocol_type", (void *)g_tsg_proto_name2id[identify_info->proto].name, TLD_TYPE_STRING);
+ TLD_append(TLD_handle, (char *)"mail_protocol_type", (void *)g_tsg_proto_name2id[proto].name, TLD_TYPE_STRING);
}
else
{
- TLD_append(TLD_handle, schema_field_name, (void *)g_tsg_proto_name2id[identify_info->proto].name, TLD_TYPE_STRING);
+ TLD_append(TLD_handle, schema_field_name, (void *)g_tsg_proto_name2id[proto].name, TLD_TYPE_STRING);
}
- switch(identify_info->proto)
+ if(domain!=NULL)
{
- case PROTO_HTTP:
- domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_HTTP_HOST);
- TLD_append(TLD_handle, domain_field_name, (void *)identify_info->domain, TLD_TYPE_STRING);
- break;
- case PROTO_SSL:
- domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_SSL_SNI);
- TLD_append(TLD_handle, domain_field_name, (void *)identify_info->domain, TLD_TYPE_STRING);
- break;
- case PROTO_QUIC:
- domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_QUIC_SNI);
- TLD_append(TLD_handle, domain_field_name, (void *)identify_info->domain, TLD_TYPE_STRING);
- break;
- default:
- break;
+ switch(proto)
+ {
+ case PROTO_HTTP:
+ domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_HTTP_HOST);
+ TLD_append(TLD_handle, domain_field_name, (void *)domain, TLD_TYPE_STRING);
+ break;
+ case PROTO_SSL:
+ domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_SSL_SNI);
+ TLD_append(TLD_handle, domain_field_name, (void *)domain, TLD_TYPE_STRING);
+ break;
+ case PROTO_QUIC:
+ domain_field_name=log_field_id2name(g_tsg_log_instance, LOG_QUIC_SNI);
+ TLD_append(TLD_handle, domain_field_name, (void *)domain, TLD_TYPE_STRING);
+ break;
+ default:
+ break;
+ }
}
}
- log_msg.a_stream=a_stream;
+ log_msg.a_stream=(struct streaminfo *)a_stream;
log_msg.result=p_result;
log_msg.result_num=result_num;
tsg_send_log(g_tsg_log_instance, TLD_handle, &log_msg, thread_seq);
if(p_result->config_id!=DECCRYPTION_EXCLUSION_ALLOW_POLICY_ID)
{
- tsg_set_policy_flow(a_stream, p_result, thread_seq);
+ tsg_set_policy_flow((struct streaminfo *)a_stream, p_result, thread_seq);
}
return 1;
@@ -340,7 +328,7 @@ static int tsg_proto_name2flag(char *proto_list, int *flag)
{
if((memcmp(s, g_tsg_proto_name2id[i].name, e-s))==0)
{
- *flag|=(1<<g_tsg_proto_name2id[i].id);
+ *flag|=(1<<g_tsg_proto_name2id[i].type);
break;
}
}
@@ -351,52 +339,30 @@ static int tsg_proto_name2flag(char *proto_list, int *flag)
return 0;
}
-int get_depolyment_mode(void)
+static void free_context_label(int thread_seq, void *project_req_value)
{
- int ret=0, len=0;
- char buff[32]={0};
-
- len=sizeof(buff);
- ret=sapp_get_platform_opt(SPO_DEPLOYMENT_MODE_STR, buff, &len);
- if(ret>=0)
- {
- if((memcmp(buff, "mirror", strlen(buff)))==0)
- {
- g_tsg_para.depolyment_mode=0;
- }
- else
- {
- g_tsg_para.depolyment_mode=1;
- }
- }
-
- return 0;
+ return ;
}
-void free_app_id_label(int thread_seq, void *project_req_value)
+static void free_policy_label(int thread_seq, void *project_req_value)
{
if(project_req_value!=NULL)
{
dictator_free(thread_seq, project_req_value);
+ project_req_value=NULL;
}
}
-static void free_policy_label(int thread_seq, void *project_req_value)
+void free_gather_app_result(int thread_seq, void *project_req_value)
{
- dictator_free(thread_seq, project_req_value);
- project_req_value=NULL;
-}
-
-static void copy_identify_info(struct master_context *context, struct identify_info *identify_info, int thread_seq)
-{
- if(identify_info->proto>PROTO_UNKONWN && identify_info->proto<PROTO_MAX && identify_info->domain_len>0 && strlen(identify_info->domain)>0)
+ if(project_req_value!=NULL)
{
- memcpy(context->domain, identify_info->domain, identify_info->domain_len);
- context->domain_len=identify_info->domain_len;
+ dictator_free(thread_seq, project_req_value);
+ project_req_value=NULL;
}
}
-static void copy_monitor_result(struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int result_num, int thread_seq)
+static void copy_monitor_result(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int result_num, int thread_seq)
{
int i=0;
@@ -434,21 +400,23 @@ static void copy_monitor_result(struct streaminfo *a_stream, struct master_conte
}
-static void copy_intercept_result(struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, struct identify_info *identify_info, int thread_seq)
+static void copy_intercept_result(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, char *domain, tsg_protocol_t proto, int thread_seq)
{
int ret=0;
policy_priority_label_t *priority_label=NULL;
priority_label=(policy_priority_label_t *)dictator_malloc(thread_seq, sizeof(policy_priority_label_t));
-
+ memset(priority_label, 0, sizeof(policy_priority_label_t));
+
+ priority_label->proto=proto;
+ priority_label->domain_len=MIN(sizeof(priority_label->domain)-1 ,strlen(domain));
+ memcpy(priority_label->domain, domain, priority_label->domain_len);
+
priority_label->result_num=1;
priority_label->result_type=PULL_KNI_RESULT;
- priority_label->proto=identify_info->proto;
- priority_label->domain_len=identify_info->domain_len;
- memcpy(priority_label->domain, identify_info->domain, identify_info->domain_len);
memcpy(priority_label->result, p_result, sizeof(struct Maat_rule_t));
- ret=project_req_add_struct(a_stream, g_tsg_para.priority_project_id, (void *)priority_label);
+ ret=project_req_add_struct((struct streaminfo *)a_stream, g_tsg_para.priority_project_id, (void *)priority_label);
if(ret<0)
{
free_policy_label(thread_seq, (void *)priority_label);
@@ -474,7 +442,7 @@ static void copy_intercept_result(struct streaminfo *a_stream, struct master_con
return ;
}
-static void copy_deny_result(struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int thread_seq)
+static void copy_deny_result(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int thread_seq)
{
if(context->result==NULL)
{
@@ -502,7 +470,7 @@ static void copy_deny_result(struct streaminfo *a_stream, struct master_context
return ;
}
-static void copy_bypass_result(struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int thread_seq)
+static void copy_bypass_result(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *p_result, int thread_seq)
{
if(context->result==NULL)
{
@@ -541,25 +509,145 @@ static void copy_bypass_result(struct streaminfo *a_stream, struct master_contex
return ;
}
-int is_intercept_exclusion(struct streaminfo *a_stream, Maat_rule_t *p_result, struct identify_info *identify_info, int thread_seq)
+static unsigned char deal_deny_action(const struct streaminfo *a_stream, struct master_context *context, Maat_rule_t *p_result)
+{
+ int ret=0,opt_value=0;
+ struct rst_tcp_para rst_paras;
+ unsigned char state=APP_STATE_GIVEME;
+ int method_type=TSG_METHOD_TYPE_UNKNOWN;
+ struct compile_user_region *user_region=NULL;
+
+ user_region=(struct compile_user_region *)Maat_rule_get_ex_data(g_tsg_maat_feather, p_result, g_tsg_para.table_id[TABLE_SECURITY_COMPILE]);
+ if(user_region!=NULL)
+ {
+ method_type=tsg_get_method_id(user_region->method);
+ switch(method_type)
+ {
+ case TSG_METHOD_TYPE_DROP:
+ set_drop_stream(a_stream);
+ //copy_deny_result(a_stream, context, p_result, a_stream->threadnum);
+ state=APP_STATE_DROPPKT|APP_STATE_DROPME;
+ break;
+ case TSG_METHOD_TYPE_RESET:
+ if(a_stream->type==STREAM_TYPE_TCP)
+ {
+ rst_paras.rst_pkt_num=1;
+ rst_paras.signature_seed1=65535;
+ rst_paras.signature_seed2=13;
+ rst_paras.th_flags=4;
+ rst_paras.__pad_no_use=0;
+ rst_paras.dir=DIR_DOUBLE;
+ ret=MESA_rst_tcp((struct streaminfo *)a_stream, &rst_paras, sizeof(rst_paras));
+ if(ret<0)
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger,
+ RLOG_LV_FATAL,
+ "RST_TCP",
+ "Send RST failed policy_id: %d service: %d action: %d addr: %s",
+ p_result->config_id,
+ p_result->service_id,
+ (unsigned char)p_result->action,
+ PRINTADDR(a_stream, g_tsg_para.level)
+ );
+ }
+
+ opt_value=1;
+ MESA_set_stream_opt(a_stream, MSO_TCP_RST_REMEDY, (void *)&opt_value, sizeof(opt_value));
+ }
+
+ set_drop_stream(a_stream);
+ //copy_deny_result(a_stream, context, p_result, a_stream->threadnum);
+ state=APP_STATE_DROPPKT|APP_STATE_DROPME;
+ break;
+ case TSG_METHOD_TYPE_BLOCK:
+ case TSG_METHOD_TYPE_ALERT:
+ case TSG_METHOD_TYPE_REDIRECTION:
+ break;
+ default:
+ break;
+ }
+
+ security_compile_free(g_tsg_para.table_id[TABLE_SECURITY_COMPILE], p_result, NULL, (MAAT_RULE_EX_DATA *)&user_region, 0, NULL);
+ }
+
+ return state;
+}
+
+static int l7_protocol_mapper(const char *filename)
+{
+ int ret=0;
+ FILE *fp=NULL;
+ char line[1024]={0};
+ char type_name[32]={0};
+ struct l7_protocol *protocol=NULL;
+
+ fp=fopen(filename, "r");
+ if(fp==NULL)
+ {
+ printf("Open %s failed ...", filename);
+ return -1;
+ }
+
+ memset(line, 0, sizeof(line));
+
+ while((fgets(line, sizeof(line), fp))!=NULL)
+ {
+ if(line[0]=='#' || line[0]=='\n' || line[0]=='\r' ||line[0]=='\0')
+ {
+ continue;
+ }
+
+ protocol=(struct l7_protocol *)calloc(1, sizeof(struct l7_protocol));
+ ret=sscanf(line, "%s %s %d", type_name, protocol->name, &protocol->id);
+ assert(ret==3);
+
+ HASH_ADD(hh1, g_tsg_para.name_by_id, id, sizeof(int), protocol);
+ HASH_ADD(hh2, g_tsg_para.id_by_name, name, strlen(protocol->name), protocol);
+
+ memset(line, 0, sizeof(line));
+ }
+
+ fclose(fp);
+ fp=NULL;
+
+ return 1;
+}
+
+char *tsg_l7_protocol_id2name(unsigned int l7_protocol_id)
+{
+ struct l7_protocol *l7_proto=NULL;
+ HASH_FIND(hh1, g_tsg_para.name_by_id, &l7_protocol_id, sizeof(l7_protocol_id), l7_proto);
+ if(l7_proto!=NULL)
+ {
+ return l7_proto->name;
+ }
+
+ return NULL;
+}
+
+unsigned int tsg_l7_protocol_name2id(const char *l7_protocol_name)
+{
+ struct l7_protocol *l7_proto=NULL;
+
+ HASH_FIND(hh2, g_tsg_para.id_by_name, l7_protocol_name, strlen(l7_protocol_name), l7_proto);
+ if(l7_proto!=NULL)
+ {
+ return l7_proto->id;
+ }
+
+ return 0;
+}
+
+
+int is_intercept_exclusion(const struct streaminfo *a_stream, Maat_rule_t *p_result, char *domain, int thread_seq)
{
int ret=0;
scan_status_t mid=NULL;
Maat_rule_t tmp_result;
- if(identify_info!=NULL && identify_info->domain_len>0)
+ if(domain!=NULL)
{
- ret=Maat_full_scan_string(g_tsg_maat_feather,
- g_tsg_para.table_id[TABLE_EXCLUSION_SSL_SNI],
- CHARSET_UTF8,
- identify_info->domain,
- identify_info->domain_len,
- &tmp_result,
- NULL,
- 1,
- &mid,
- thread_seq);
-
+ ret=Maat_full_scan_string(g_tsg_maat_feather, g_tsg_para.table_id[TABLE_EXCLUSION_SSL_SNI], CHARSET_UTF8, domain, strlen(domain), &tmp_result, NULL, 1, &mid,thread_seq);
if(mid!=NULL)
{
Maat_clean_status(&mid);
@@ -572,7 +660,7 @@ int is_intercept_exclusion(struct streaminfo *a_stream, Maat_rule_t *p_result, s
RLOG_LV_DEBUG,
"EXCLUSION_SSL_SNI",
"Hit %s policy_id: %d service: %d action: %d Decryption Exclusion: [ policy_id: %d service: %d action: %d ] addr: %s",
- identify_info->domain,
+ domain,
tmp_result.config_id,
tmp_result.service_id,
(unsigned char)tmp_result.action,
@@ -584,47 +672,49 @@ int is_intercept_exclusion(struct streaminfo *a_stream, Maat_rule_t *p_result, s
return 1;
}
- else
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "EXCLUSION_SSL_SNI",
- "Not hit %s stream_dir: %d addr: %s scan ret: %d",
- identify_info->domain,
- a_stream->dir,
- PRINTADDR(a_stream, g_tsg_para.level),
- ret
- );
- }
+
+ MESA_handle_runtime_log(g_tsg_para.logger,
+ RLOG_LV_DEBUG,
+ "EXCLUSION_SSL_SNI",
+ "Not hit %s stream_dir: %d addr: %s scan ret: %d",
+ domain,
+ a_stream->dir,
+ PRINTADDR(a_stream, g_tsg_para.level),
+ ret
+ );
}
return 0;
}
-void close_stream_free_context(struct streaminfo *a_stream, struct master_context *context, int thread_seq)
+static int scan_fqdn_category_id(Maat_feather_t maat_feather, const struct streaminfo *a_stream, char *domain, Maat_rule_t *result, int result_num, scan_status_t *mid, int table_id, int thread_seq)
{
- struct identify_info identify_info;
+ int scan_ret=0;
+ struct _session_attribute_label_t *attribute_label=NULL;
+
+ attribute_label=(struct _session_attribute_label_t *)project_req_get_struct(a_stream, g_tsg_para.internal_project_id);
+ if(attribute_label!=NULL && domain!=NULL && table_id>=0)
+ {
+ attribute_label->fqdn_category_id_num=tsg_get_fqdn_category_id(g_tsg_maat_feather, domain, attribute_label->fqdn_category_id, MAX_CATEGORY_ID_NUM, g_tsg_para.logger, thread_seq);
+ scan_ret=tsg_scan_fqdn_category_id(g_tsg_maat_feather, a_stream, result, result_num, mid, table_id, attribute_label->fqdn_category_id, attribute_label->fqdn_category_id_num, thread_seq);
+ }
+ return scan_ret;
+}
+
+void close_stream_free_context(const struct streaminfo *a_stream, struct master_context *context, int thread_seq)
+{
if(context!=NULL)
{
if(context->hit_cnt>0 && context->result!=NULL)
{
- memset(&identify_info, 0, sizeof(identify_info));
- if(context->proto==PROTO_UNKONWN || context->proto>PROTO_APP)
- {
- identify_info.proto=PROTO_APP;
- }
- else
- {
- identify_info.proto=context->proto;
- }
+ master_send_log(a_stream, context->result, context->hit_cnt, context->domain, context->proto, thread_seq);
+ }
- if(context->domain_len>0)
- {
- memcpy(identify_info.domain, context->domain, context->domain_len);
- identify_info.domain_len=context->domain_len;
- }
- master_send_log(a_stream, context->result, context->hit_cnt, &identify_info, thread_seq);
+ if(context->domain!=NULL)
+ {
+ dictator_free(thread_seq, (void *)context->domain);
+ context->domain=NULL;
}
if(context->result!=NULL)
@@ -646,7 +736,7 @@ void close_stream_free_context(struct streaminfo *a_stream, struct master_contex
return ;
}
-void set_session_attribute_label(struct streaminfo *a_stream, enum TSG_ATTRIBUTE_TYPE type, void *value, int thread_seq)
+void set_session_attribute_label(const struct streaminfo *a_stream, enum TSG_ATTRIBUTE_TYPE type, void *value, int thread_seq)
{
struct timespec tv;
unsigned long long create_time=0;
@@ -660,9 +750,12 @@ void set_session_attribute_label(struct streaminfo *a_stream, enum TSG_ATTRIBUTE
attribute_label=(struct _session_attribute_label_t *)dictator_malloc(thread_seq, sizeof(struct _session_attribute_label_t));
memset(attribute_label, 0, sizeof(struct _session_attribute_label_t));
- ret=project_req_add_struct(a_stream, g_tsg_para.internal_project_id, (const void *)attribute_label);
+ ret=project_req_add_struct((struct streaminfo *)a_stream, g_tsg_para.internal_project_id, (const void *)attribute_label);
if(ret<0)
{
+ dictator_free(thread_seq, (void *)attribute_label);
+ attribute_label=NULL;
+
MESA_handle_runtime_log(g_tsg_para.logger,
RLOG_LV_FATAL,
"PROJECT_ADD",
@@ -671,6 +764,7 @@ void set_session_attribute_label(struct streaminfo *a_stream, enum TSG_ATTRIBUTE
attribute_label->proto,
PRINTADDR(a_stream, g_tsg_para.level)
);
+ return ;
}
}
@@ -688,7 +782,7 @@ void set_session_attribute_label(struct streaminfo *a_stream, enum TSG_ATTRIBUTE
attribute_label->proto=(tsg_protocol_t)(*(int *)value);
break;
case TSG_ATTRIBUTE_TYPE_JA3_HASH:
- ja3_info=ssl_get_ja3_fingerprint(a_stream, (unsigned char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, a_stream->threadnum);
+ ja3_info=ssl_get_ja3_fingerprint((struct streaminfo *)a_stream, (unsigned char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, a_stream->threadnum);
if(ja3_info!=NULL)
{
if(attribute_label!=NULL && ja3_info->fp!=NULL && ja3_info->fp_len>0)
@@ -706,64 +800,6 @@ void set_session_attribute_label(struct streaminfo *a_stream, enum TSG_ATTRIBUTE
return ;
}
-char *tsg_schema_index2string(tsg_protocol_t proto)
-{
- char *schema_field_value=NULL;
-
- switch(proto)
- {
- case PROTO_HTTP:
- schema_field_value=(char *)"HTTP";
- break;
- case PROTO_SSL:
- schema_field_value=(char *)"SSL";
- break;
- case PROTO_DNS:
- schema_field_value=(char *)"DNS";
- break;
- case PROTO_FTP:
- schema_field_value=(char *)"FTP";
- break;
- case PROTO_BGP:
- schema_field_value=(char *)"BGP";
- break;
- case PROTO_SIP:
- schema_field_value=(char *)"SIP";
- break;
- case PROTO_MAIL:
- schema_field_value=(char *)"MAIL";
- break;
- case PROTO_STREAMING_MEDIA:
- schema_field_value=(char *)"STREAMING_MEDIA";
- break;
- case PROTO_QUIC:
- schema_field_value=(char *)"QUIC";
- break;
- case PROTO_SSH:
- schema_field_value=(char *)"SSH";
- break;
- case PROTO_IMAP:
- schema_field_value=(char *)"IMAP";
- break;
- case PROTO_POP3:
- schema_field_value=(char *)"POP3";
- break;
- case PROTO_SMTP:
- schema_field_value=(char *)"SMTP";
- break;
- case PROTO_RTP:
- schema_field_value=(char *)"RTP";
- break;
- case PROTO_APP:
- schema_field_value=(char *)"APP";
- break;
- default:
- break;
- }
-
- return schema_field_value;
-}
-
int tsg_set_device_id_to_telegraf(char *device_sn)
{
char buff[128]={0};
@@ -838,7 +874,7 @@ static void free_session_attribute_label(int thread_seq, void *project_req_value
}
}
-struct Maat_rule_t *tsg_policy_decision_criteria(struct streaminfo *a_stream, Maat_rule_t *result, int result_num, struct identify_info *identify_info, int thread_seq)
+struct Maat_rule_t *tsg_policy_decision_criteria(Maat_rule_t *result, int result_num)
{
int i=0;
Maat_rule_t *p_result=NULL;
@@ -869,10 +905,11 @@ struct Maat_rule_t *tsg_policy_decision_criteria(struct streaminfo *a_stream, Ma
return p_result;
}
-static int identify_application_protocol(struct streaminfo *a_stream, struct identify_info *identify_info, void *a_packet)
+static int identify_application_protocol(const struct streaminfo *a_stream, struct master_context *context, void *a_packet)
{
- int ret=0;
- identify_info->proto = PROTO_UNKONWN;
+ int ret=0, length=0;
+ char buff[4096]={0};
+ context->proto = PROTO_UNKONWN;
switch(a_stream->type)
{
@@ -880,18 +917,15 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
if(g_tsg_para.proto_flag&(1<<PROTO_HTTP)) //http
{
char *host=NULL;
- ret=http_host_parser((char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, a_stream->curdir, &host);
- if(ret>=0)
+ length=http_host_parser((char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, a_stream->curdir, &host);
+ if(length>=0)
{
- identify_info->proto=PROTO_HTTP;
- if(ret>0 && host!=NULL)
+ context->proto=PROTO_HTTP;
+ if(length>0 && host!=NULL)
{
- identify_info->domain_len=MIN(ret, (int)sizeof(identify_info->domain) - 1);
- strncpy(identify_info->domain, host, identify_info->domain_len);
- }
- else
- {
- identify_info->domain_len=0;
+ context->domain=(char *)dictator_malloc(a_stream->threadnum, length+1);
+ memset(context->domain, 0, length+1);
+ memcpy(context->domain, host, length);
}
return 1;
}
@@ -905,16 +939,16 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
chello=ssl_chello_parse((unsigned char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, &chello_status);
if(chello_status==CHELLO_PARSE_SUCCESS)
{
- identify_info->proto=PROTO_SSL;
- if(chello->sni==NULL)
- {
- identify_info->domain_len = 0;
- }
- else
+ context->proto=PROTO_SSL;
+ if(chello->sni!=NULL)
{
- identify_info->domain_len = strnlen(chello->sni, sizeof(identify_info->domain) - 1);
- strncpy(identify_info->domain, chello->sni, identify_info->domain_len);
+ length=strlen(chello->sni);
+ context->domain=(char *)dictator_malloc(a_stream->threadnum, length+1);
+ memset(context->domain, 0, length+1);
+ memcpy(context->domain, chello->sni, length);
}
+
+ context->is_esni=(int)chello->is_encrypt_sni;
ssl_chello_free(chello);
return 1;
@@ -925,31 +959,31 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
if(g_tsg_para.proto_flag&(1<<PROTO_FTP)) //ftp
{
- ret=ftp_control_identify(a_stream);
+ ret=ftp_control_identify((struct streaminfo *)a_stream);
if(ret>0)
{
- identify_info->proto=PROTO_FTP;
+ context->proto=PROTO_FTP;
return 1;
}
}
if(g_tsg_para.proto_flag&(1<<PROTO_MAIL)) //mail
{
- ret=mail_protocol_identify_by_first_payload(a_stream,(char *)a_stream->ptcpdetail->pdata, a_stream->ptcpdetail->datalen, a_stream->threadnum);
+ ret=mail_protocol_identify_by_first_payload((struct streaminfo *)a_stream,(char *)a_stream->ptcpdetail->pdata, a_stream->ptcpdetail->datalen, a_stream->threadnum);
if(ret>0)
{
switch(ret)
{
case SMTP_PROTOCOL:
- identify_info->proto=PROTO_SMTP;
+ context->proto=PROTO_SMTP;
return 1;
break;
case POP3_PROTOCOL:
- identify_info->proto=PROTO_POP3;
+ context->proto=PROTO_POP3;
return 1;
break;
case IMAP_PROTOCOL:
- identify_info->proto=PROTO_IMAP;
+ context->proto=PROTO_IMAP;
return 1;
break;
default:
@@ -971,7 +1005,7 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
tpl4=a_stream->addr.tuple4_v4;
if((ntohs(tpl4->source)==53) || (ntohs(tpl4->dest)==53))
{
- identify_info->proto=PROTO_DNS;
+ context->proto=PROTO_DNS;
return 1;
}
break;
@@ -979,7 +1013,7 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
tpl6=a_stream->addr.tuple4_v6;
if((ntohs(tpl6->source)==53) || (ntohs(tpl6->dest)==53))
{
- identify_info->proto=PROTO_DNS;
+ context->proto=PROTO_DNS;
return 1;
}
break;
@@ -993,21 +1027,38 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
ret = ssh_protocol_identify((unsigned char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen,g_tsg_para.logger);
if(ret > 0)
{
- identify_info->proto=PROTO_SSH;
+ context->proto=PROTO_SSH;
return 1;
}
}
if(g_tsg_para.proto_flag&(1<<PROTO_QUIC)) //quic
{
- ret=quic_protocol_identify(a_stream, a_packet, identify_info->domain, sizeof(identify_info->domain));
- if(ret>=0)
+ length=quic_protocol_identify((struct streaminfo *)a_stream, a_packet, buff, sizeof(buff));
+ if(length>=0)
{
- identify_info->proto=PROTO_QUIC;
- identify_info->domain_len=ret;
+ context->proto=PROTO_QUIC;
+ if(length>0 && strlen(buff)>0)
+ {
+ context->domain=(char *)dictator_malloc(a_stream->threadnum, length+1);
+ memset(context->domain, 0, length+1);
+ memcpy(context->domain, buff, length);
+ }
return 1;
}
}
+
+ if(g_tsg_para.proto_flag&(1<<PROTO_SIP))
+ {
+ unsigned char sip_ret=0;
+ char *from=NULL, *to=NULL;
+ unsigned int from_len=0, to_len=0;
+ sip_ret=sip_identify_from_to((char *)a_stream->ptcpdetail->pdata, (unsigned int)a_stream->ptcpdetail->datalen, &from, &from_len, &to, &to_len);
+ if(sip_ret==SIP_TRUE)
+ {
+ context->proto=PROTO_SIP;
+ }
+ }
break;
default:
break;
@@ -1016,17 +1067,16 @@ static int identify_application_protocol(struct streaminfo *a_stream, struct ide
return ret;
}
-int scan_application_id_and_properties(struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *result, int result_num, scan_status_t *mid, int thread_seq)
+int scan_application_id_and_properties(const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, struct app_identify_result *identify_result, int thread_seq)
{
- int hit_num=0;
+ int i=0,hit_num=0;
+ char *name=NULL;
char app_id_buff[32]={0};
- struct app_id_label *app_id_label=NULL;
struct app_id_dict_table *dict=NULL;
- app_id_label=get_app_id_label(a_stream, context, thread_seq);
- if(app_id_label!=NULL)
+ for(i=0; i< identify_result->app_id_num; i++)
{
- snprintf(app_id_buff, sizeof(app_id_buff), "%d", app_id_label->app_id);
+ snprintf(app_id_buff, sizeof(app_id_buff), "%d", identify_result->app_id[i]);
dict=(struct app_id_dict_table *)Maat_plugin_get_EX_data(g_tsg_maat_feather, g_tsg_para.table_id[TABLE_APP_ID_DICT], (const char *)app_id_buff);
if(dict!=NULL)
{
@@ -1035,180 +1085,60 @@ int scan_application_id_and_properties(struct streaminfo *a_stream, struct maste
hit_num+=tsg_scan_app_properties_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, dict->technology, (char *)"technology", thread_seq);
hit_num+=tsg_scan_app_properties_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, dict->subcategroy, (char *)"subcategroy", thread_seq);
hit_num+=tsg_scan_app_properties_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, dict->characteristics, (char *)"characteristics", thread_seq);
-
- if(dict->continue_scanning==APP_SCAN_FLAG_STOP)
- {
- context->continue_scan_app_id=APP_SCAN_FLAG_STOP;
- }
- hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, dict->app_name, app_id_label->app_id, thread_seq);
+ hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, dict->app_name, identify_result->app_id[i], thread_seq);
}
else
- {
- hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, (char *)"", app_id_label->app_id, thread_seq);
+ {
+ name=tsg_l7_protocol_id2name(identify_result->app_id[i]);
+ hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, ((name==NULL) ? (char *)"" : name), identify_result->app_id[i], thread_seq);
}
}
return hit_num;
}
-extern "C" char TSG_MASTER_TCP_ENTRY(struct streaminfo *a_tcp, void **pme, int thread_seq,void *a_packet)
-{
- int opt_value=0;
- int proto_id=0;
- char *l7_protocol=NULL;
- int ret=0,hit_num=0;
- int state=APP_STATE_GIVEME;
+static unsigned char master_deal_scan_result(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *result, int hit_num, void *a_packet)
+{
Maat_rule_t *p_result=NULL;
- struct identify_info identify_info;
- Maat_rule_t result[MAX_RESULT_NUM];
- struct rst_tcp_para rst_paras;
- struct compile_user_region *user_region=NULL;
- int method_type=TSG_METHOD_TYPE_UNKNOWN;
- struct master_context *context=(struct master_context *)*pme;
-
- if(*pme==NULL)
- {
- init_context(pme, thread_seq);
- context=(struct master_context *)*pme;
- }
-
- switch(a_tcp->opstate)
- {
- case OP_STATE_PENDING:
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_TCP_LINKS], 0, FS_OP_ADD, 1);
- set_session_attribute_label(a_tcp, TSG_ATTRIBUTE_TYPE_ESTABLISH_LATECY, NULL, thread_seq);
-
- memset(&identify_info, 0, sizeof(identify_info));
- ret=identify_application_protocol(a_tcp, &identify_info, a_packet);
- if(ret==1)
- {
- copy_identify_info(context, &identify_info, thread_seq);
- set_session_attribute_label(a_tcp, TSG_ATTRIBUTE_TYPE_PROTOCOL, (void *)(&identify_info.proto), thread_seq);
-
- context->proto=identify_info.proto;
-
- if(identify_info.proto==PROTO_SSL)
- {
- set_session_attribute_label(a_tcp, TSG_ATTRIBUTE_TYPE_JA3_HASH, NULL, thread_seq);
- }
- else
- {
- context->continue_scan_proto_id=APP_SCAN_FLAG_STOP;
- }
-
- hit_num+=tsg_scan_shared_policy(g_tsg_maat_feather, a_tcp, &identify_info, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, thread_seq);
- }
-
- ret=tsg_scan_nesting_addr(g_tsg_maat_feather, a_tcp, identify_info.proto, &context->mid, result+hit_num, MAX_RESULT_NUM-hit_num);
- if(ret>0)
- {
- hit_num+=ret;
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_HIT_ADDR], 0, FS_OP_ADD, 1);
- }
- if((is_only_monitor(result, hit_num)) && identify_info.proto!=PROTO_UNKONWN) // business deal action of monitor
- {
- hit_num=0;
- }
- break;
- default:
- break;
- }
+ unsigned char state=APP_STATE_GIVEME;
- if(context->proto==PROTO_UNKONWN || context->proto==PROTO_SSL || context->proto>PROTO_APP || context->continue_scan_proto_id==APP_SCAN_FLAG_CONTINUE) /* support block/alert(deny), Do action in fw_http_plug */
- {
- proto_id=get_basic_proto_id(a_tcp, context, thread_seq);
- if(proto_id>0 && proto_id!=context->basic_proto_id)
- {
- context->proto=((context->proto==PROTO_SSL) ? PROTO_SSL : PROTO_APP);
- context->basic_proto_id=proto_id;
- l7_protocol=tsg_l7_protocol_id2name(g_tsg_log_instance, proto_id);
- if(l7_protocol==NULL && proto_id==g_tsg_para.mail_proto_id)
- {
- l7_protocol=(char *)"MAIL";
- }
-
- hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_tcp, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, l7_protocol, proto_id, thread_seq);
- }
- }
-
- hit_num+=scan_application_id_and_properties(a_tcp, context, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, thread_seq);
- p_result=tsg_policy_decision_criteria(a_tcp, result, hit_num, &identify_info, thread_seq);
+ p_result=tsg_policy_decision_criteria(result, hit_num);
if(g_tsg_para.default_compile_switch==1 && p_result==NULL)
{
if(get_default_policy(g_tsg_para.default_compile_id, &result[0]))
{
p_result=&result[0];
- context->is_default_policy=1;
}
}
-
+
if(p_result!=NULL)
- {
+ {
switch((unsigned char)p_result->action)
{
case TSG_ACTION_DENY:
- user_region=(struct compile_user_region *)Maat_rule_get_ex_data(g_tsg_maat_feather, p_result, g_tsg_para.table_id[TABLE_SECURITY_COMPILE]);
- if(user_region!=NULL)
+ state=deal_deny_action(a_stream, context, p_result);
+ if((state&APP_STATE_DROPPKT)==APP_STATE_DROPPKT)
{
- method_type=tsg_get_method_id(user_region->method);
- switch(method_type)
- {
- case TSG_METHOD_TYPE_DROP:
- opt_value=1;
- MESA_set_stream_opt(a_tcp, MSO_DROP_STREAM, (void *)&opt_value, sizeof(opt_value));
- MESA_set_stream_opt(a_tcp, MSO_TIMEOUT, (void *)&g_tsg_para.timeout, sizeof(g_tsg_para.timeout));
- copy_deny_result(a_tcp, context, p_result, thread_seq);
- state=APP_STATE_DROPPKT|APP_STATE_DROPME;
- break;
- case TSG_METHOD_TYPE_RESET:
- rst_paras.rst_pkt_num=1;
- rst_paras.signature_seed1=65535;
- rst_paras.signature_seed2=13;
- rst_paras.th_flags=4;
- rst_paras.__pad_no_use=0;
- rst_paras.dir=DIR_DOUBLE;
- ret=MESA_rst_tcp(a_tcp, &rst_paras, sizeof(rst_paras));
-
- opt_value=1;
- MESA_set_stream_opt(a_tcp, MSO_TCP_RST_REMEDY, (void *)&opt_value, sizeof(opt_value));
-
- if(g_tsg_para.depolyment_mode>0)
- {
- opt_value=1;
- MESA_set_stream_opt(a_tcp, MSO_DROP_STREAM, (void *)&opt_value, sizeof(opt_value));
- MESA_set_stream_opt(a_tcp, MSO_TIMEOUT, (void *)&g_tsg_para.timeout, sizeof(g_tsg_para.timeout));
- }
- copy_deny_result(a_tcp, context, p_result, thread_seq);
- state=APP_STATE_DROPPKT|APP_STATE_DROPME;
- break;
- case TSG_METHOD_TYPE_BLOCK:
- case TSG_METHOD_TYPE_ALERT:
- case TSG_METHOD_TYPE_REDIRECTION:
- break;
- default:
- break;
- }
-
- security_compile_free(g_tsg_para.table_id[TABLE_SECURITY_COMPILE], p_result, NULL, (MAAT_RULE_EX_DATA *)&user_region, 0, NULL);
+ master_send_log(a_stream, p_result, 1, context->domain, context->proto, a_stream->threadnum);
}
break;
case TSG_ACTION_MONITOR:
- copy_monitor_result(a_tcp, context, result, hit_num, thread_seq);
+ copy_monitor_result(a_stream, context, result, hit_num, a_stream->threadnum);
break;
case TSG_ACTION_BYPASS:
- copy_bypass_result(a_tcp, context, p_result, thread_seq);
+ copy_bypass_result(a_stream, context, p_result, a_stream->threadnum);
FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_BYPASS], 0, FS_OP_ADD, 1);
state=APP_STATE_GIVEME|APP_STATE_KILL_OTHER;
break;
case TSG_ACTION_INTERCEPT:
- if(is_intercept_exclusion(a_tcp, p_result, &identify_info, thread_seq))
+ if(is_intercept_exclusion(a_stream, p_result, context->domain, a_stream->threadnum))
{
FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_EXCLUSION], 0, FS_OP_ADD, 1);
break;
}
- copy_intercept_result(a_tcp, context, p_result, &identify_info, thread_seq);
+ copy_intercept_result(a_stream, context, p_result, context->domain, context->proto, a_stream->threadnum);
FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_INTERCEPT], 0, FS_OP_ADD, 1);
state=APP_STATE_DROPME|APP_STATE_KILL_OTHER;
break;
@@ -1217,151 +1147,184 @@ extern "C" char TSG_MASTER_TCP_ENTRY(struct streaminfo *a_tcp, void **pme, int t
}
}
- if((a_tcp->opstate==OP_STATE_CLOSE) || (state&APP_STATE_DROPME)==APP_STATE_DROPME)
- {
- close_stream_free_context(a_tcp, context, thread_seq);
- *pme=NULL;
- }
-
return state;
}
-extern "C" char TSG_MASTER_UDP_ENTRY(struct streaminfo *a_udp, void **pme, int thread_seq,void *a_packet)
+static int app_identify_result_cb(const struct streaminfo *a_stream, int bridge_id, void *data)
{
- int ret=0,hit_num=0;
- int opt_value=0,proto_id=0;
- char *l7_protocol=NULL;
- int state=APP_STATE_GIVEME;
- Maat_rule_t *p_result=NULL;
- Maat_rule_t result[MAX_RESULT_NUM]={0};
- struct identify_info identify_info;
- struct compile_user_region *user_region=NULL;
- int method_type=TSG_METHOD_TYPE_UNKNOWN;
- struct master_context *context=(struct master_context *)*pme;
+ int hit_num=0;
+ struct master_context *context=NULL;
+ struct Maat_rule_t scan_result[MAX_RESULT_NUM];
+ struct gather_app_result *gather_result=NULL;
+ struct app_identify_result *identify_result=(struct app_identify_result *)data;
- if(*pme==NULL)
+ if(data==NULL)
+ {
+ return 0;
+ }
+
+ gather_result=(struct gather_app_result *)get_struct_project(a_stream, g_tsg_para.gather_app_project_id);
+ if(gather_result==NULL)
{
- init_context(pme, thread_seq);
- context=(struct master_context *)*pme;
+ gather_result=(struct gather_app_result *)dictator_malloc(a_stream->threadnum, sizeof(struct gather_app_result));
+ memset(gather_result, 0, sizeof(struct gather_app_result));
+ set_struct_project(a_stream, g_tsg_para.gather_app_project_id, (void *)gather_result);
}
- switch(a_udp->opstate)
+
+ switch(identify_result->origin)
{
- case OP_STATE_PENDING:
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_UDP_LINKS], 0, FS_OP_ADD, 1);
+ case ORIGIN_DKPT:
+ case ORIGIN_QM_ENGINE:
+ case ORIGIN_USER_DEFINE:
+ case ORIGIN_BASIC_PROTOCOL:
+ memcpy(&(gather_result->result[identify_result->origin]), identify_result, sizeof(struct app_identify_result));
+ break;
+ default:
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "APP_BRIDGE_CB", "Unknown type: %d addr: %s", identify_result->origin, PRINTADDR(a_stream, g_tsg_para.level));
+ return 0;
+ }
- memset(&identify_info, 0, sizeof(identify_info));
- ret=identify_application_protocol(a_udp, &identify_info, a_packet);
- if(ret==1)
- {
- copy_identify_info(context, &identify_info, thread_seq);
- set_session_attribute_label(a_udp, TSG_ATTRIBUTE_TYPE_PROTOCOL, (void *)&(identify_info.proto), thread_seq);
+ context=(struct master_context *)get_struct_project(a_stream, g_tsg_para.context_project_id);
+ if(context==NULL)
+ {
+ init_context((void **)&context, a_stream->threadnum);
+ }
+
+ record_time_start(&context->last_scan_time);
+
+ hit_num=scan_application_id_and_properties((struct streaminfo *)a_stream, scan_result, MAX_RESULT_NUM, &context->mid, identify_result, a_stream->threadnum);
+ master_deal_scan_result(a_stream, context, scan_result, hit_num, NULL);
- context->proto=identify_info.proto;
- context->continue_scan_proto_id=APP_SCAN_FLAG_STOP;
- hit_num+=tsg_scan_shared_policy(g_tsg_maat_feather, a_udp, &identify_info, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, thread_seq);
- }
+ return 0;
+}
- ret=tsg_scan_nesting_addr(g_tsg_maat_feather, a_udp, identify_info.proto, &context->mid, result, MAX_RESULT_NUM);
- if(ret>0)
- {
- hit_num+=ret;
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_HIT_ADDR], 0, FS_OP_ADD, 1);
- }
- if((is_only_monitor(result, hit_num)) && identify_info.proto!=PROTO_UNKONWN) // business deal action of deny and monitor
- {
- hit_num=0;
- }
+static int master_deal_pending_state(const struct streaminfo *a_stream, struct master_context *context, struct Maat_rule_t *result, int result_num, void *a_packet)
+{
+ int table_id=0;
+ int ret=0,hit_num=0;
+ unsigned int protocol_id=0;
+
+ ret=identify_application_protocol(a_stream, context, a_packet);
+ if(ret==1)
+ {
+ set_session_attribute_label(a_stream, TSG_ATTRIBUTE_TYPE_PROTOCOL, (void *)&(context->proto), a_stream->threadnum);
- break;
- default:
- break;
+ if(context->proto==PROTO_SSL)
+ {
+ set_session_attribute_label(a_stream, TSG_ATTRIBUTE_TYPE_JA3_HASH, NULL, a_stream->threadnum);
+ }
+ else
+ {
+ context->continue_scan_proto_id=APP_SCAN_FLAG_STOP;
+ }
+
+ table_id=get_table_id(context->proto);
+ hit_num+=tsg_scan_shared_policy(g_tsg_maat_feather, a_stream, context->domain, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, table_id, a_stream->threadnum);
+ hit_num+=scan_fqdn_category_id(g_tsg_maat_feather, a_stream, context->domain, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, table_id, a_stream->threadnum);
+ if(context->is_esni)
+ {
+ protocol_id=tsg_l7_protocol_name2id("ESNI");
+ hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_stream, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, (char *)"ESNI", protocol_id, a_stream->threadnum);
+ }
}
- if(context->proto==PROTO_UNKONWN || context->proto>PROTO_APP || context->continue_scan_proto_id==APP_SCAN_FLAG_CONTINUE)
+ ret=tsg_scan_nesting_addr(g_tsg_maat_feather, a_stream, context->proto, &context->mid, result+hit_num, MAX_RESULT_NUM-hit_num);
+ if(ret>0)
{
- proto_id=get_basic_proto_id(a_udp, context, thread_seq);
- if(proto_id>0 && context->basic_proto_id!=proto_id)
- {
- if(is_repetitive_protocol_id(proto_id))
- {
- context->continue_scan_proto_id=APP_SCAN_FLAG_STOP;
- switch(proto_id)
- {
- case SIP_PROTO_ID:
- context->proto=PROTO_SIP;
- set_session_attribute_label(a_udp, TSG_ATTRIBUTE_TYPE_PROTOCOL, (void *)&(context->proto), thread_seq);
- break;
- case RTP_PROTO_ID:
- context->proto=PROTO_RTP;
- set_session_attribute_label(a_udp, TSG_ATTRIBUTE_TYPE_PROTOCOL, (void *)&(context->proto), thread_seq);
- break;
- default:
- break;
- }
- }
- else
- {
- context->proto=PROTO_APP;
- }
-
- context->basic_proto_id=proto_id;
- l7_protocol=tsg_l7_protocol_id2name(g_tsg_log_instance, proto_id);
- hit_num+=tsg_scan_app_id_policy(g_tsg_maat_feather, a_udp, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, l7_protocol, proto_id, thread_seq);
- }
+ hit_num+=ret;
+ FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_HIT_ADDR], 0, FS_OP_ADD, 1);
}
- hit_num+=scan_application_id_and_properties(a_udp, context, result+hit_num, MAX_RESULT_NUM-hit_num, &context->mid, thread_seq);
- p_result=tsg_policy_decision_criteria(a_udp, result, hit_num, &identify_info, thread_seq);
- if(g_tsg_para.default_compile_switch==1 && p_result==NULL)
+ if((is_only_monitor(result, hit_num)) && context->proto!=PROTO_UNKONWN) // business deal action of monitor
{
- if(get_default_policy(g_tsg_para.default_compile_id, &result[0]))
+ hit_num=0;
+ }
+
+ return hit_num;
+}
+
+extern "C" unsigned char TSG_MASTER_TCP_ENTRY(const struct streaminfo *a_tcp, void **pme, int thread_seq,void *a_packet)
+{
+ int hit_num=0;
+ unsigned char state=APP_STATE_GIVEME;
+ Maat_rule_t result[MAX_RESULT_NUM];
+ struct master_context *context=(struct master_context *)*pme;
+
+ if(*pme==NULL)
+ {
+ context=(struct master_context *)get_struct_project(a_tcp, g_tsg_para.context_project_id);
+ if(context==NULL)
+ {
+ init_context(pme, thread_seq);
+ context=(struct master_context *)*pme;
+ set_struct_project(a_tcp, g_tsg_para.context_project_id, *pme);
+ }
+ else
{
- p_result=&result[0];
- context->is_default_policy=1;
+ *pme=(void *)context;
}
+ record_time_start(&context->last_scan_time);
+ }
+
+ if(a_tcp->opstate==OP_STATE_PENDING)
+ {
+ FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_TCP_LINKS], 0, FS_OP_ADD, 1);
+ set_session_attribute_label(a_tcp, TSG_ATTRIBUTE_TYPE_ESTABLISH_LATECY, NULL, thread_seq);
+ hit_num+=master_deal_pending_state(a_tcp, context, result-hit_num, MAX_RESULT_NUM-hit_num, a_packet);
}
- if(p_result!=NULL)
+ if(record_time_elapse_us(&context->last_scan_time)/100000 > g_tsg_para.scan_time_interval)
{
- switch((unsigned char)p_result->action)
+ record_time_start(&context->last_scan_time);
+ }
+
+ state=master_deal_scan_result(a_tcp, context, result, hit_num, a_packet);
+ if((a_tcp->opstate==OP_STATE_CLOSE) || (state&APP_STATE_DROPME)==APP_STATE_DROPME)
+ {
+ close_stream_free_context(a_tcp, context, thread_seq);
+ *pme=NULL;
+ }
+
+ return state;
+}
+
+extern "C" unsigned char TSG_MASTER_UDP_ENTRY(const struct streaminfo *a_udp, void **pme, int thread_seq,void *a_packet)
+{
+ int hit_num=0;
+ unsigned char state=APP_STATE_GIVEME;
+ Maat_rule_t result[MAX_RESULT_NUM]={0};
+ struct master_context *context=(struct master_context *)*pme;
+
+ if(*pme==NULL)
+ {
+ context=(struct master_context *)get_struct_project(a_udp, g_tsg_para.context_project_id);
+ if(context==NULL)
{
- case TSG_ACTION_DENY:
- user_region=(struct compile_user_region *)Maat_rule_get_ex_data(g_tsg_maat_feather, p_result, g_tsg_para.table_id[TABLE_SECURITY_COMPILE]);
- if(user_region!=NULL)
- {
- method_type=tsg_get_method_id(user_region->method);
- switch(method_type)
- {
- case TSG_METHOD_TYPE_RESET:
- case TSG_METHOD_TYPE_DROP:
- opt_value=1;
- MESA_set_stream_opt(a_udp, MSO_DROP_STREAM, (void *)&opt_value, sizeof(opt_value));
- MESA_set_stream_opt(a_udp, MSO_TIMEOUT, (void *)&g_tsg_para.timeout, sizeof(g_tsg_para.timeout));
- break;
- default:
- break;
- }
- security_compile_free(g_tsg_para.table_id[TABLE_SECURITY_COMPILE], p_result, NULL, (MAAT_RULE_EX_DATA *)&user_region, 0, NULL);
- }
- copy_deny_result(a_udp, context, p_result, thread_seq);
- state=APP_STATE_DROPPKT|APP_STATE_DROPME;
- break;
- case TSG_ACTION_MONITOR:
- copy_monitor_result(a_udp, context, result, hit_num, thread_seq);
- break;
- case TSG_ACTION_BYPASS:
- copy_bypass_result(a_udp, context, p_result, thread_seq);
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_BYPASS], 0, FS_OP_ADD, 1);
- state=APP_STATE_GIVEME|APP_STATE_KILL_OTHER;
- break;
- case TSG_ACTION_INTERCEPT:
- break;
- default:
- break;
+ init_context(pme, thread_seq);
+ context=(struct master_context *)*pme;
+ set_struct_project(a_udp, g_tsg_para.context_project_id, *pme);
}
+ else
+ {
+ *pme=(void *)context;
+ }
+ record_time_start(&context->last_scan_time);
+ }
+
+ if(a_udp->opstate==OP_STATE_PENDING)
+ {
+ FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_UDP_LINKS], 0, FS_OP_ADD, 1);
+ hit_num+=master_deal_pending_state(a_udp, context, result-hit_num, MAX_RESULT_NUM-hit_num, a_packet);
}
+ if(record_time_elapse_us(&context->last_scan_time)/100000 > g_tsg_para.scan_time_interval)
+ {
+ record_time_start(&context->last_scan_time);
+ }
+
+ state=master_deal_scan_result(a_udp, context, result, hit_num, a_packet);
+
if((a_udp->opstate==OP_STATE_CLOSE) || (state&APP_STATE_DROPME)==APP_STATE_DROPME)
{
close_stream_free_context(a_udp, context, thread_seq);
@@ -1373,6 +1336,7 @@ extern "C" char TSG_MASTER_UDP_ENTRY(struct streaminfo *a_udp, void **pme, int t
extern "C" int TSG_MASTER_INIT()
{
int i=0,ret=0;
+ char buff[128]={0};
int value=0,cycle=0;
int output_prometheus=0;
unsigned short fs_server_port=0;
@@ -1384,8 +1348,6 @@ extern "C" int TSG_MASTER_INIT()
char identify_proto_name[MAX_STRING_LEN*4]={0};
memset(&g_tsg_para, 0, sizeof(g_tsg_para));
-
- get_depolyment_mode();
MESA_load_profile_int_def(tsg_conffile, "SYSTEM","LOG_LEVEL", &g_tsg_para.level, RLOG_LV_FATAL);
MESA_load_profile_string_def(tsg_conffile, "SYSTEM","LOG_PATH", g_tsg_para.log_path, sizeof(g_tsg_para.log_path), "tsglog/tsg_master");
@@ -1401,12 +1363,13 @@ extern "C" int TSG_MASTER_INIT()
MESA_load_profile_int_def(tsg_conffile, "SYSTEM","DEFAULT_POLICY_ID", &g_tsg_para.default_compile_id, 0);
MESA_load_profile_int_def(tsg_conffile, "SYSTEM","DEFAULT_POLICY_SWITCH", &g_tsg_para.default_compile_switch, 0);
- MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "IDENTIFY_PROTO_NAME", identify_proto_name, sizeof(identify_proto_name), "HTTP;SSL;DNS;FTP;BGP;SIP;MAIL;STREAMING_MEDIA;QUIC;");
+ MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "IDENTIFY_PROTO_NAME", identify_proto_name, sizeof(identify_proto_name), "HTTP;SSL;DNS;FTP;BGP;SIP;MAIL;STREAMING_MEDIA;QUIC;SIP;");
tsg_proto_name2flag(identify_proto_name, &g_tsg_para.proto_flag);
MESA_load_profile_int_def(tsg_conffile, "SYSTEM", "ENTRANCE_ID", &g_tsg_para.entrance_id, 0);
MESA_load_profile_short_def(tsg_conffile, "SYSTEM", "TIMEOUT", (short *)&g_tsg_para.timeout, 300);
MESA_load_profile_int_def(tsg_conffile, "SYSTEM", "MAIL_PROTOCOL_ID",&(g_tsg_para.mail_proto_id), 110);
+ MESA_load_profile_int_def(tsg_conffile, "SYSTEM", "SCAN_TIME_INTERVAL", &g_tsg_para.scan_time_interval, 10);
MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "DEVICE_ID_COMMAND", g_tsg_para.device_id_command, sizeof(g_tsg_para.device_id_command), NULL);
g_tsg_para.device_id=get_device_id(g_tsg_para.device_id_command, g_tsg_para.entrance_id);
@@ -1428,13 +1391,7 @@ extern "C" int TSG_MASTER_INIT()
g_tsg_para.priority_project_id=project_producer_register(label_buff, PROJECT_VAL_TYPE_STRUCT, free_policy_label);
if(g_tsg_para.priority_project_id<0)
{
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_FATAL,
- "PROJECT_REGISTER",
- "Register %s failed; please check :%s and add <POLICY_PRIORITY struct>",
- label_buff,
- "etc/project_list.conf"
- );
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "PROJECT_REGISTER", "Register %s failed.", label_buff);
return -1;
}
@@ -1442,37 +1399,35 @@ extern "C" int TSG_MASTER_INIT()
g_tsg_para.internal_project_id=project_producer_register(label_buff, PROJECT_VAL_TYPE_STRUCT, free_session_attribute_label);
if(g_tsg_para.internal_project_id<0)
{
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_FATAL,
- "PROJECT_REGISTER",
- "Register %s failed; please check :%s and add <TSG_MASTER_INTERNAL_LABEL struct>",
- label_buff,
- "etc/project_list.conf"
- );
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "PROJECT_REGISTER", "Register %s failed.", label_buff);
}
- MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "APP_ID_LABEL", label_buff, sizeof(label_buff), "APP_ID_LABEL");
- g_tsg_para.app_id_project_id=project_producer_register(label_buff, PROJECT_VAL_TYPE_STRUCT, free_app_id_label);
- if(g_tsg_para.app_id_project_id<0)
+ MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "APP_BRIDGE_NAME", label_buff, sizeof(label_buff), "APP_BRIDGE");
+ g_tsg_para.app_bridge_id=stream_bridge_build(label_buff, "w");
+ if(g_tsg_para.app_bridge_id<0)
{
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_FATAL,
- "APP_ID_LABEL",
- "project_customer_register is error, app_id_label: %s, please check etc/project.conf",
- label_buff
- );
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "APP_BRIDGE", "stream_bridge_build is error, app_bridge_name: %s", label_buff);
}
- MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "L7_PROTO_LABEL", label_buff, sizeof(label_buff), "BASIC_PROTO_LABEL");
- g_tsg_para.l7_proto_project_id=project_customer_register(label_buff, "struct");
- if(g_tsg_para.l7_proto_project_id<0)
+ ret=stream_bridge_register_data_sync_cb(g_tsg_para.app_bridge_id, app_identify_result_cb);
+ if(ret<0)
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "APP_BRIDGE", "Register callback failed, app_bridge_id: %d", g_tsg_para.app_bridge_id);
+ return -1;
+ }
+
+ g_tsg_para.context_project_id=project_producer_register("TSG_MASTER_CONTEXT", PROJECT_VAL_TYPE_STRUCT, free_context_label);
+ if(g_tsg_para.context_project_id<0)
{
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_FATAL,
- "L7_PROTO_LABEL",
- "project_customer_register is error, l7_proto_label: %s, please check etc/project.conf",
- label_buff
- );
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "CONTEXT_LABEL", "project_customer_register is error, context label: %s","TSG_MASTER_CONTEXT");
+ return -1;
+ }
+
+ g_tsg_para.gather_app_project_id=project_producer_register("APP_IDENTIFY_RESULT", PROJECT_VAL_TYPE_STRUCT, free_gather_app_result);
+ if(g_tsg_para.gather_app_project_id<0)
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "APP_IDENTIFY_RESULT", "project_customer_register is error, context label: %s","APP_IDENTIFY_RESULT");
+ return -1;
}
ret=tsg_rule_init(tsg_conffile, g_tsg_para.logger);
@@ -1525,7 +1480,6 @@ extern "C" int TSG_MASTER_INIT()
g_tsg_para.fs2_field_id[i]=FS_register(g_tsg_para.fs2_handle, FS_STYLE_FIELD, FS_CALC_SPEED, g_tsg_fs2_field[i].name);
}
- char buff[32]={0};
int thread_num=get_thread_count();
for(i=0; i<thread_num && g_tsg_log_instance!=NULL; i++)
{
@@ -1546,6 +1500,9 @@ extern "C" int TSG_MASTER_INIT()
MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_FATAL, "INIT_STATISTIC", "tsg_statistic_init failed ...");
return -1;
}
+
+ MESA_load_profile_string_def(tsg_conffile, "SYSTEM", "L7_RPTOCOL_FILE", buff, sizeof(buff), "./tsgconf/tsg_l7_protocol.conf");
+ l7_protocol_mapper(buff);
return 0;
}
diff --git a/src/tsg_entry.h b/src/tsg_entry.h
index cfe06ce..d0041a7 100644
--- a/src/tsg_entry.h
+++ b/src/tsg_entry.h
@@ -3,7 +3,10 @@
#include <MESA/Maat_rule.h>
#include <MESA/field_stat2.h>
+
+#include "uthash.h"
#include "tsg_rule.h"
+#include "app_label.h"
#include "tsg_label.h"
#include "tsg_statistic.h"
@@ -28,15 +31,6 @@ typedef int atomic_t;
#define PRINTADDR(a, b) ((b)<RLOG_LV_FATAL ? printaddr(&(a->addr), a->threadnum) : "")
#endif
-#define DNS_PROTO_ID 103
-#define FTP_PROTO_ID 104
-#define HTTP_PROTO_ID 106
-#define MAIL_PROTO_ID 110
-#define QUIC_PROTO_ID 119
-#define SIP_PROTO_ID 120
-#define SSL_PROTO_ID 126
-#define RTP_PROTO_ID 142
-
#define APP_SCAN_FLAG_STOP 0
#define APP_SCAN_FLAG_CONTINUE 1
@@ -101,6 +95,20 @@ struct _str2index
char *type;
};
+struct gather_app_result
+{
+ struct app_identify_result result[ORIGIN_MAX];
+};
+
+struct l7_protocol
+{
+ int id; /* first key */
+ char name[32]; /* second key */
+ UT_hash_handle hh1; /* handle for first hash table */
+ UT_hash_handle hh2; /* handle for second hash table */
+};
+
+
struct _fqdn_category_t
{
int ref_cnt;
@@ -114,14 +122,13 @@ struct master_context
tsg_protocol_t proto;
int hit_cnt;
int app_id;
- int is_default_policy;
- char continue_scan_app_id;
+ int is_esni;
char continue_scan_proto_id;
unsigned short basic_proto_id;
- int domain_len;
- char domain[MAX_DOAMIN_LEN];
+ char *domain;
struct Maat_rule_t *result;
- scan_status_t mid;
+ scan_status_t mid;
+ struct timespec last_scan_time;
};
#define _MAX_TABLE_NAME_LEN 64
@@ -130,18 +137,19 @@ typedef struct _tsg_para
int level;
int mail_proto_id;
unsigned short timeout;
- unsigned short depolyment_mode;
int app_id_table_type;
int device_id;
int entrance_id;
+ int scan_time_interval;
int default_compile_switch;
int default_compile_id;
int table_id[TABLE_MAX];
int dyn_subscribe_ip_table_id; //TSG_DYN_SUBSCRIBER_IP
int priority_project_id;
int internal_project_id;
- int l7_proto_project_id;
- int app_id_project_id;
+ int context_project_id;
+ int gather_app_project_id;
+ int app_bridge_id;
int proto_flag; //tsg_protocol_t
int fs2_field_id[TSG_FS2_MAX];
char device_sn[MAX_DOAMIN_LEN/8];
@@ -151,6 +159,8 @@ typedef struct _tsg_para
char table_name[TABLE_MAX][_MAX_TABLE_NAME_LEN];
void *logger;
screen_stat_handle_t fs2_handle;
+ struct l7_protocol *name_by_id;
+ struct l7_protocol *id_by_name;
}g_tsg_para_t;
extern g_tsg_para_t g_tsg_para;
@@ -244,11 +254,14 @@ void location_free_data(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void*
void ASN_free_data(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp);
void subscribe_id_free_data(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void* argp);
void security_compile_free(int idx, const struct Maat_rule_t* rule, const char* srv_def_large, MAAT_RULE_EX_DATA* ad, long argl, void *argp);
-char *tsg_schema_index2string(tsg_protocol_t proto);
-struct Maat_rule_t *tsg_policy_decision_criteria(struct streaminfo *a_stream, Maat_rule_t *result, int result_num, struct _identify_info *identify_info, int thread_seq);
-int tsg_scan_shared_policy(Maat_feather_t maat_feather, struct streaminfo *a_stream, struct identify_info *identify_info, Maat_rule_t *result, int result_num, scan_status_t *mid, int thread_seq);
+struct Maat_rule_t *tsg_policy_decision_criteria(struct streaminfo *a_stream, Maat_rule_t *result, int result_num, int thread_seq);
+int tsg_scan_shared_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, char *domain, Maat_rule_t *result, int result_num, scan_status_t *mid, int table_id, int thread_seq);
int tsg_scan_app_id_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *name, unsigned int id, int thread_seq);
-int tsg_scan_app_properties_policy(Maat_feather_t maat_feather, struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *property, char *district, int thread_seq);
+int tsg_scan_app_properties_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *property, char *district, int thread_seq);
int tsg_scan_subscribe_id_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, struct _subscribe_id_info_t *user_info, int thread_seq);
+int tsg_scan_fqdn_category_id(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, int table_id, unsigned int *category_id, int category_id_num, int thread_seq);
+unsigned int tsg_l7_protocol_name2id(const char *l7_protocol_name);
+char *tsg_l7_protocol_id2name(unsigned int l7_protocol_id);
+
#endif
diff --git a/src/tsg_rule.cpp b/src/tsg_rule.cpp
index ceafc0d..b75af05 100644
--- a/src/tsg_rule.cpp
+++ b/src/tsg_rule.cpp
@@ -12,6 +12,7 @@
#include "Maat_command.h"
#include "MESA/http.h"
#include "tsg_rule.h"
+#include "tsg_label.h"
#include "tsg_entry.h"
#include "tsg_send_log.h"
#include "tsg_send_log_internal.h"
@@ -76,35 +77,6 @@ static char* str_unescape(char* s)
return s;
}
-static int proto_str2id(tsg_protocol_t proto)
-{
- switch(proto)
- {
- case PROTO_TCP: return 100;
- case PROTO_UDP: return 101;
- case PROTO_HTTP: return 106;
- case PROTO_MAIL: return 110;
- case PROTO_IMAP: return 151;
- case PROTO_POP3: return 116;//116
- case PROTO_SMTP: return 122;//122
- case PROTO_DNS: return 103;
- case PROTO_FTP: return 104;
- case PROTO_SSL: return 126;
- case PROTO_SIP: return 120;
- case PROTO_QUIC: return 119;
- case PROTO_SSH: return 125;
- case PROTO_RTP: return 142;
- case PROTO_IPv6:
- case PROTO_IPv4:
- case PROTO_STREAMING_MEDIA:
- case PROTO_BGP:
- default:
- break;
- }
-
- return 0;
-}
-
static int get_data_center(char *accept_tag, char *effective_tag_key, char *data_center, int data_center_len)
{
int i=0,len;
@@ -954,43 +926,34 @@ int tsg_pull_policy_result(struct streaminfo *a_stream, PULL_RESULT_TYPE pull_re
{
if(label->result_type==pull_result_type)
{
- num=(label->result_num>result_num) ? result_num : label->result_num;
+ num=MIN(label->result_num, result_num);
memcpy(result, label->result, num*sizeof(Maat_rule_t));
-
- memcpy(identify_info->domain, label->domain, label->domain_len);
- identify_info->domain_len=label->domain_len;
+ if(label->domain_len>0)
+ {
+ memcpy(identify_info->domain, label->domain, label->domain_len);
+ identify_info->domain_len=label->domain_len;
+ }
+
identify_info->proto = label->proto;
return num;
}
- else
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "PULL_RESULT",
- "pull policy failed, hit: %s %s: %s policy_id: %d service: %d action: %d addr: %s",
- (label->result_type==PULL_KNI_RESULT) ? "KNI" : "FW",
- label->proto==PROTO_HTTP ? "host" : "sni",
- label->domain,
- label->result->config_id,
- label->result->service_id,
- label->result->action,
- PRINTADDR(a_stream, g_tsg_para.level)
- );
- }
- }
- else
- {
+
MESA_handle_runtime_log(g_tsg_para.logger,
RLOG_LV_DEBUG,
- "PULL_RESULT",
- "pull policy failed, Not hit, label is %s addr: %s",
- (label==NULL) ? "NULL" : label->domain,
+ "PULL_RESULT",
+ "pull policy failed, hit: %s %s: %s policy_id: %d service: %d action: %d addr: %s",
+ (label->result_type==PULL_KNI_RESULT) ? "KNI" : "FW",
+ label->proto==PROTO_HTTP ? "host" : "sni",
+ label->domain,
+ label->result->config_id,
+ label->result->service_id,
+ label->result->action,
PRINTADDR(a_stream, g_tsg_para.level)
);
}
-
+
return 0;
}
@@ -1192,11 +1155,11 @@ int tsg_scan_ip_location(Maat_feather_t maat_feather, const struct streaminfo *a
int tsg_scan_nesting_addr(Maat_feather_t maat_feather, const struct streaminfo *a_stream, tsg_protocol_t proto, scan_status_t *mid, Maat_rule_t*result, int result_num)
{
int ret=0;
- struct ipaddr t_addr;
unsigned int proto_id=0;
+ struct ipaddr t_addr;
struct ipaddr* p_addr=NULL;
int hit_num=0,tans_proto=0;
- int is_scan_addr=1, maat_ret=0;
+ int is_scan_addr=1, maat_ret=0;
const struct streaminfo *cur_stream = a_stream;
struct _session_attribute_label_t *attribute_label=NULL;
@@ -1274,11 +1237,11 @@ int tsg_scan_nesting_addr(Maat_feather_t maat_feather, const struct streaminfo *
if(hit_num<result_num && proto>PROTO_UNKONWN && proto<PROTO_MAX)
{
- proto_id=proto_str2id(proto);
+ proto_id=tsg_l7_protocol_name2id(g_tsg_proto_name2id[proto].name);
hit_num+=tsg_scan_app_id_policy(maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, g_tsg_proto_name2id[proto].name, proto_id, (int)a_stream->threadnum);
if(proto==PROTO_SMTP || proto==PROTO_IMAP || proto==PROTO_POP3)
{
- proto_id=proto_str2id(PROTO_MAIL);
+ proto_id=tsg_l7_protocol_name2id(g_tsg_proto_name2id[PROTO_MAIL].name);
hit_num+=tsg_scan_app_id_policy(maat_feather, a_stream, result+hit_num, result_num-hit_num, mid, g_tsg_proto_name2id[PROTO_MAIL].name, proto_id, (int)a_stream->threadnum);
}
}
@@ -1332,133 +1295,36 @@ int tsg_scan_nesting_addr(Maat_feather_t maat_feather, const struct streaminfo *
//return value: -1: failed, 0: not hit, >0: hit count
-int tsg_scan_shared_policy(Maat_feather_t maat_feather, struct streaminfo *a_stream, struct identify_info *identify_info, Maat_rule_t *result, int result_num, scan_status_t *mid, int thread_seq)
+int tsg_scan_shared_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, char *domain, Maat_rule_t *result, int result_num, scan_status_t *mid, int table_id, int thread_seq)
{
- char *field_name=NULL;
- int i=0,ret=0,idx=0,hit_num=0;
- struct _session_attribute_label_t *attribute_label=NULL;
+ int ret=0;
- if(identify_info->proto!=PROTO_UNKONWN && strlen(identify_info->domain)>0 && identify_info->domain_len>0)
+ if(table_id<0 || domain==NULL)
{
- switch(identify_info->proto)
- {
- case PROTO_HTTP:
- idx=TABLE_HTTP_HOST;
- field_name=(char *)"http_host";
- break;
- case PROTO_SSL:
- idx=TABLE_SSL_SNI;
- field_name=(char *)"ssl_sni";
- break;
- case PROTO_QUIC:
- idx=TABLE_QUIC_SNI;
- field_name=(char *)"quic_sni";
- break;
- default:
- return 0;
- break;
- }
-
- ret=Maat_full_scan_string(g_tsg_maat_feather,
- g_tsg_para.table_id[idx],
- CHARSET_UTF8,
- identify_info->domain,
- identify_info->domain_len,
- result,
- NULL,
- result_num,
- mid,
- thread_seq
+ return 0;
+ }
+
+ ret=Maat_full_scan_string(g_tsg_maat_feather, table_id, CHARSET_UTF8, domain, strlen(domain), result, NULL, result_num, mid, thread_seq);
+ if(ret>0)
+ {
+ FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_HIT_SHARE], 0, FS_OP_ADD, 1);
+ MESA_handle_runtime_log(g_tsg_para.logger,
+ RLOG_LV_DEBUG,
+ "SCAN_FQDN",
+ "Hit %s policy_id: %d service: %d action: %d addr: %s",
+ domain,
+ result[0].config_id,
+ result[0].service_id,
+ (unsigned char)result[0].action,
+ PRINTADDR(a_stream, g_tsg_para.level)
);
- if(ret>0)
- {
- FS_operate(g_tsg_para.fs2_handle, g_tsg_para.fs2_field_id[TSG_FS2_HIT_SHARE], 0, FS_OP_ADD, 1);
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "SCAN_FQDN",
- "Hit %s: %s policy_id: %d service: %d action: %d addr: %s",
- field_name,
- identify_info->domain,
- result[hit_num].config_id,
- result[hit_num].service_id,
- (unsigned char)result[hit_num].action,
- PRINTADDR(a_stream, g_tsg_para.level)
- );
- hit_num+=ret;
- }
- else
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "SCAN_FQDN",
- "Not hit %s: %s ret: %d stream_dir: %d addr: %s",
- (ret==-1) ? "NULL" : ((identify_info->proto==PROTO_HTTP) ? "host" : "sni"),
- (ret==-1) ? "NULL" : identify_info->domain,
- ret,
- a_stream->dir,
- PRINTADDR(a_stream, g_tsg_para.level)
- );
- }
-
- attribute_label=(struct _session_attribute_label_t *)project_req_get_struct(a_stream, g_tsg_para.internal_project_id);
- if(attribute_label!=NULL)
- {
- attribute_label->fqdn_category_id_num=tsg_get_fqdn_category_id(g_tsg_maat_feather,
- identify_info->domain,
- attribute_label->fqdn_category_id,
- MAX_CATEGORY_ID_NUM,
- g_tsg_para.logger,
- thread_seq
- );
-
-
- for(i=0; i<attribute_label->fqdn_category_id_num; i++)
- {
- int idx=identify_info->proto==PROTO_HTTP ? TABLE_HTTP_HOST : TABLE_SSL_SNI;
- ret=Maat_scan_intval(g_tsg_maat_feather,
- g_tsg_para.table_id[idx],
- (unsigned int)attribute_label->fqdn_category_id[i],
- result+hit_num,
- result_num-hit_num,
- mid,
- thread_seq
- );
- if(ret>0)
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "SCAN_FQDN_CAT",
- "Hit %s: %s category_id: %d policy_id: %d service: %d action: %d addr: %s",
- (identify_info->proto==PROTO_HTTP) ? "host" : "sni",
- identify_info->domain,
- attribute_label->fqdn_category_id[i],
- result[hit_num].config_id,
- result[hit_num].service_id,
- (unsigned char)result[hit_num].action,
- PRINTADDR(a_stream, g_tsg_para.level)
- );
- hit_num+=ret;
- }
- else
- {
- MESA_handle_runtime_log(g_tsg_para.logger,
- RLOG_LV_DEBUG,
- "SCAN_FQDN_CAT",
- "Not hit %s: %s category_id: %d ret: %d stream_dir: %d addr: %s",
- (ret==-1) ? "NULL" : ((identify_info->proto==PROTO_HTTP) ? "host" : "sni"),
- (ret==-1) ? "NULL" : identify_info->domain,
- attribute_label->fqdn_category_id[i],
- ret,
- a_stream->dir,
- PRINTADDR(a_stream, g_tsg_para.level)
- );
- }
- }
- }
+ return ret;
}
-
- return hit_num;
+
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_DEBUG, "SCAN_FQDN", "Not hit %s ret: %d stream_dir: %d addr: %s", domain, ret, a_stream->dir, PRINTADDR(a_stream, g_tsg_para.level));
+
+ return 0;
}
@@ -1551,6 +1417,41 @@ int tsg_get_fqdn_category_id(Maat_feather_t maat_feather, char *fqdn, unsigned i
return 0;
}
+int tsg_scan_fqdn_category_id(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, int table_id, unsigned int *category_id, int category_id_num, int thread_seq)
+{
+ int i=0,ret=0,hit_num=0;
+
+ if(table_id<0 || result_num<=0 || category_id==NULL)
+ {
+ return 0;
+ }
+
+ for(i=0; i<category_id_num; i++)
+ {
+ ret=Maat_scan_intval(g_tsg_maat_feather, table_id, (unsigned int)category_id[i], result+hit_num, result_num-hit_num, mid, thread_seq);
+ if(ret>0)
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger,
+ RLOG_LV_DEBUG,
+ "SCAN_FQDN_CAT",
+ "Hit category_id: %d policy_id: %d service: %d action: %d addr: %s",
+ category_id[i],
+ result[hit_num].config_id,
+ result[hit_num].service_id,
+ (unsigned char)result[hit_num].action,
+ PRINTADDR(a_stream, g_tsg_para.level)
+ );
+ hit_num+=ret;
+ }
+ else
+ {
+ MESA_handle_runtime_log(g_tsg_para.logger, RLOG_LV_DEBUG, "SCAN_FQDN_CAT", "Not hit category_id: %d ret: %d addr: %s", category_id[i], ret, PRINTADDR(a_stream, g_tsg_para.level));
+ }
+ }
+
+ return hit_num;
+}
+
int tsg_scan_app_id_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *name, unsigned int id, int thread_seq)
{
@@ -1580,7 +1481,7 @@ int tsg_scan_app_id_policy(Maat_feather_t maat_feather, const struct streaminfo
return 0;
}
-int tsg_scan_app_properties_policy(Maat_feather_t maat_feather, struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *property, char *district, int thread_seq)
+int tsg_scan_app_properties_policy(Maat_feather_t maat_feather, const struct streaminfo *a_stream, struct Maat_rule_t *result, int result_num, scan_status_t *mid, char *property, char *district, int thread_seq)
{
int i=0,ret=0;
int ret2=0, hit_num=0;
diff --git a/src/tsg_send_log.cpp b/src/tsg_send_log.cpp
index 052edd3..d4fd245 100644
--- a/src/tsg_send_log.cpp
+++ b/src/tsg_send_log.cpp
@@ -73,165 +73,356 @@ static int is_tunnels(struct streaminfo *a_stream)
return is_tunnel;
}
-static int convert_mac_to_string(unsigned char *mac, char *buff)
+static int set_isn(struct TLD_handle_t *_handle, struct streaminfo *a_stream, char *field_name, enum MESA_stream_opt type)
{
- int i=0,len=0;
+ int ret=0;
+ unsigned int isn=0;
+ int size=sizeof(unsigned long long);
+
+ size=sizeof(unsigned int);
+ ret=MESA_get_stream_opt(a_stream, type, &isn, &size);
+ if(ret==0)
+ {
+ TLD_append(_handle, field_name, (void *)(long)isn, TLD_TYPE_LONG);
+ }
+
+ return 1;
+}
+
+static int set_tcp_isn(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
+{
+ if(a_stream->type==STREAM_TYPE_TCP)
+ {
+ switch(a_stream->dir)
+ {
+ case DIR_C2S:
+ set_isn(_handle, a_stream, _instance->id2field[LOG_COMMON_TCP_CLIENT_ISN].name, MSO_TCP_ISN_C2S);
+ break;
+ case DIR_S2C:
+ set_isn(_handle, a_stream, _instance->id2field[LOG_COMMON_TCP_SERVER_ISN].name, MSO_TCP_ISN_S2C);
+ break;
+ case DIR_DOUBLE:
+ set_isn(_handle, a_stream, _instance->id2field[LOG_COMMON_TCP_CLIENT_ISN].name, MSO_TCP_ISN_C2S);
+ set_isn(_handle, a_stream, _instance->id2field[LOG_COMMON_TCP_SERVER_ISN].name, MSO_TCP_ISN_S2C);
+ break;
+ default:
+ break;
+ }
+ }
- for(i=0; i<6; i++)
+ return 1;
+}
+static int set_direction(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
+{
+ int direction=0,i_or_e=0;
+
+ i_or_e=MESA_dir_link_to_human(a_stream->routedir);
+ switch(a_stream->curdir)
{
- len+=sprintf(buff+len, "%02x:", mac[i]);
+ case DIR_C2S:
+ if(i_or_e=='E' || i_or_e=='e')
+ {
+ direction='E';
+ }
+ else
+ {
+ direction='I';
+ }
+ break;
+ case DIR_S2C:
+ if(i_or_e=='E' || i_or_e=='e')
+ {
+ direction='I';
+ }
+ else
+ {
+ direction='E';
+ }
+ break;
+ default:
+ break;
}
- buff[len-1]='\0';
+
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_DIRECTION].name, (void *)(long)direction, TLD_TYPE_LONG);
- return 0;
+ return 1;
}
-static int action2fs_id(int action)
+static int set_address_list(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
- switch(action)
+ int ret=0;
+ unsigned short tunnel_type=0;
+ char nest_addr_buf[1024];
+ int tunnel_type_size=sizeof(tunnel_type);
+
+ ret=MESA_get_stream_opt(a_stream, MSO_STREAM_TUNNEL_TYPE, &tunnel_type, &tunnel_type_size);
+ assert(ret==0);
+ if(tunnel_type==STREAM_TUNNLE_NON)
+ {
+ layer_addr_ntop_r(a_stream,nest_addr_buf, sizeof(nest_addr_buf));
+ }
+ else
+ {
+ stream_addr_list_ntop(a_stream,nest_addr_buf, sizeof(nest_addr_buf));
+ }
+
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_ADDRESS_LIST].name, (void *)nest_addr_buf, TLD_TYPE_STRING);
+
+ return 1;
+}
+
+static int set_tuple4(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
+{
+ int addr_type=0;
+ unsigned short c_port=0, s_port=0;
+ struct layer_addr_ipv4 *ipv4=NULL;
+ struct layer_addr_ipv6 *ipv6=NULL;
+ char server_ip[MAX_IPV4_LEN*8]={0};
+ char client_ip[MAX_IPV4_LEN*8]={0};
+
+ switch(a_stream->addr.addrtype)
{
- case TSG_ACTION_DENY:
- return TSG_FS2_ABORT_DENY;
- break;
- case TSG_ACTION_BYPASS:
- return TSG_FS2_ABORT_ALLOW;
- break;
- case TSG_ACTION_MONITOR:
- return TSG_FS2_ABORT_MONITOR;
+ case ADDR_TYPE_IPV4:
+ case __ADDR_TYPE_IP_PAIR_V4:
+ ipv4=a_stream->addr.ipv4;
+ addr_type=4;
+ c_port=ntohs(ipv4->source);
+ s_port=ntohs(ipv4->dest);
+
+ inet_ntop(AF_INET, (void *)&ipv4->saddr, client_ip, sizeof(client_ip));
+ inet_ntop(AF_INET, (void *)&ipv4->daddr, server_ip, sizeof(server_ip));
break;
- case TSG_ACTION_INTERCEPT:
- return TSG_FS2_ABORT_INTERCEPT;
+ case ADDR_TYPE_IPV6:
+ case __ADDR_TYPE_IP_PAIR_V6:
+ ipv6=a_stream->addr.ipv6;
+ addr_type=6;
+ c_port=ntohs(ipv6->source);
+ s_port=ntohs(ipv6->dest);
+
+ inet_ntop(AF_INET6, (void *)ipv6->saddr, client_ip, sizeof(client_ip));
+ inet_ntop(AF_INET6, (void *)ipv6->daddr, server_ip, sizeof(server_ip));
break;
default:
- return TSG_FS2_ABORT_UNKNOWN;
break;
}
- return TSG_FS2_ABORT_UNKNOWN;
+
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_SERVER_IP].name, (void *)server_ip, TLD_TYPE_STRING);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_CLIENT_IP].name, (void *)client_ip, TLD_TYPE_STRING);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_SERVER_PORT].name, (void *)(long)s_port, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_CLIENT_PORT].name, (void *)(long)c_port, TLD_TYPE_LONG);
+
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_STREAM_DIR].name, (void *)(long)a_stream->dir, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_ADDRESS_TYPE].name, (void *)(long)addr_type, TLD_TYPE_LONG);
+
+ return 1;
}
-int is_multi_hit_same_policy(struct Maat_rule_t *result, int *policy_id, int *policy_id_num)
+
+static int set_duraction(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
- int j=0;
-
- for(j=0;j<*policy_id_num;j++)
+ int ret=0;
+ struct timespec tv;
+ long common_con_duration_ms=0;
+ unsigned long long create_time=0;
+ int size=sizeof(unsigned long long);
+
+ if(a_stream->ptcpdetail!=NULL)
{
- if(policy_id[j]==result->config_id)
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_START_TIME].name, (void *)(a_stream->ptcpdetail->createtime), TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_END_TIME].name, (void *)(a_stream->ptcpdetail->lastmtime), TLD_TYPE_LONG);
+
+ ret=MESA_get_stream_opt(a_stream, MSO_STREAM_CREATE_TIMESTAMP_MS, (void *)&create_time, &size);
+ if(ret>=0)
{
- return 1;
+ clock_gettime(CLOCK_REALTIME, &tv);
+ common_con_duration_ms=tv.tv_sec*1000+tv.tv_nsec/1000/1000 - create_time;
+ }
+
+ if(common_con_duration_ms>0)
+ {
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_CON_DURATION_MS].name, (void *)(common_con_duration_ms), TLD_TYPE_LONG);
}
}
+ else
+ {
+ time_t cur_time=time(NULL);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_START_TIME].name, (void *)cur_time, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_END_TIME].name, (void *)cur_time, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_CON_DURATION_MS].name, (void *)(common_con_duration_ms), TLD_TYPE_LONG);
+ }
+
+ return 1;
+}
- policy_id[(*policy_id_num)++]=result->config_id;
- return 0;
-}
-unsigned long long tsg_get_stream_id(struct streaminfo * a_stream)
+static int set_packet_bytes(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
- int ret=0;
- int device_id_size=sizeof(unsigned long long);
- unsigned long long device_id=(unsigned long long)g_tsg_para.device_id;
-
- ret=MESA_get_stream_opt(a_stream, MSO_GLOBAL_STREAM_ID, (void *)&device_id, &device_id_size);
- if(ret==0)
+ struct tcp_flow_stat *tflow_project=NULL;
+ struct udp_flow_stat *uflow_project=NULL;
+
+ switch(a_stream->type)
{
- return device_id;
+ case STREAM_TYPE_TCP:
+ tflow_project=(struct tcp_flow_stat *)project_req_get_struct(a_stream, _instance->tcp_flow_project_id);
+ if(tflow_project!=NULL)
+ {
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_PKT_NUM].name, (void *)(long)tflow_project->S2C_all_pkt, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_BYTE_NUM].name, (void *)(long)tflow_project->S2C_all_byte_raw, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_PKT_NUM].name, (void *)(long)tflow_project->C2S_all_pkt, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_BYTE_NUM].name, (void *)(long)tflow_project->C2S_all_byte_raw, TLD_TYPE_LONG);
+ }
+ break;
+ case STREAM_TYPE_UDP:
+ uflow_project=(struct udp_flow_stat *)project_req_get_struct(a_stream, _instance->udp_flow_project_id);
+ if(uflow_project!=NULL)
+ {
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_PKT_NUM].name, (void *)(long)uflow_project->S2C_pkt, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_BYTE_NUM].name, (void *)(long)uflow_project->S2C_all_byte_raw, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_PKT_NUM].name, (void *)(long)uflow_project->C2S_pkt, TLD_TYPE_LONG);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_BYTE_NUM].name, (void *)(long)uflow_project->C2S_all_byte_raw, TLD_TYPE_LONG);
+ }
+ break;
+ default:
+ break;
}
- return -1;
+ return 1;
}
-int TLD_cancel(struct TLD_handle_t *handle)
+static int get_l7_protocol(struct app_identify_result *result, char *protocol_list, int protocol_list_len, int *flag)
{
- struct TLD_handle_t *_handle=handle;
+ int i=0,offset=0;
+ char *name=NULL;
+
+ if((*flag)==1)
+ {
+ return 0;
+ }
- if(_handle!=NULL)
+ for(i=0; i<result->app_id_num; i++)
{
- if(_handle->object!=NULL)
+ (*flag)=1;
+ name=tsg_l7_protocol_id2name(result->app_id[i]);
+ if(name!=NULL)
{
- cJSON_Delete(_handle->object);
- _handle->object=NULL;
+ offset+=snprintf(protocol_list+offset, protocol_list_len-offset, "%s", name);
}
-
- free(handle);
- handle=NULL;
}
-
- return 0;
+
+ return 1;
}
-int TLD_delete(struct TLD_handle_t *handle, char *key)
+static int get_app_id_list(struct app_identify_result *result, char *app_list, int app_list_len, char *surrogate_list, int surrogate_list_len, int *flag)
{
- struct TLD_handle_t *_handle=handle;
+ int i=0;
+ int offset1=0,offset2=0;
+
+ if((*flag)==1)
+ {
+ return 0;
+ }
- if(_handle!=NULL && key!=NULL)
+ for(i=0; i<result->app_id_num; i++)
{
- cJSON_DeleteItemFromObject(_handle->object, key);
+ (*flag)=1;
+ offset1+=snprintf(app_list+offset1, app_list_len-offset1, "%d;", result->app_id[i]);
+ offset2+=snprintf(surrogate_list+offset2, surrogate_list_len-offset2, "%d;", result->surrogate_id[i]);
}
- return 0;
+ return 1;
}
-int TLD_append(struct TLD_handle_t *handle, char *key, void *value, TLD_TYPE type)
+static int set_app_id(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
- struct TLD_handle_t *_handle=handle;
+ int app_id_flag=0;
+ int l7_protocol_flag=0;
+ char app_list[256]={0};
+ char protocol_list[256]={0};
+ char surrogate_list[256]={0};
+ struct gather_app_result *label=NULL;
- if(_handle==NULL || key==NULL || (value==NULL && type!=TLD_TYPE_LONG))
+ label=(struct gather_app_result *)project_req_get_struct(a_stream, g_tsg_para.gather_app_project_id);
+ if(label!=NULL)
+ {
+ get_l7_protocol(&(label->result[ORIGIN_BASIC_PROTOCOL]), protocol_list, sizeof(protocol_list), &l7_protocol_flag);
+ get_app_id_list(&(label->result[ORIGIN_USER_DEFINE]), app_list, sizeof(app_list), surrogate_list, sizeof(surrogate_list), &app_id_flag);
+ if(app_id_flag!=1)
+ {
+ get_app_id_list(&(label->result[ORIGIN_DKPT]), app_list, sizeof(app_list), surrogate_list, sizeof(surrogate_list), &app_id_flag);
+ }
+
+ if(app_id_flag!=1)
+ {
+ get_app_id_list(&(label->result[ORIGIN_QM_ENGINE]), app_list, sizeof(app_list), surrogate_list, sizeof(surrogate_list), &app_id_flag);
+ }
+
+ if(app_id_flag==1)
+ {
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_APP_ID].name, (void *)app_list, TLD_TYPE_STRING);
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_APP_SURROGATE_ID].name, (void *)surrogate_list, TLD_TYPE_STRING);
+ }
+
+ if(l7_protocol_flag==1)
+ {
+ TLD_append(_handle, _instance->id2field[LOG_COMMON_L7_PROTOCOL].name, (void *)protocol_list, TLD_TYPE_STRING);
+ }
+ }
+
+ return 1;
+}
+
+static int set_vlan(struct tsg_log_instance_t *_instance, struct single_layer_vlan_addr *vlan_addr, int layer_num, cJSON *tunnel_object, tsg_log_field_id_t id)
+{
+ if(layer_num==0)
{
- return -1;
+ return 0;
}
- switch(type)
+ int i=0;
+ cJSON *vlan_array=cJSON_CreateArray();
+ for(i=0; i<layer_num; i++)
{
- case TLD_TYPE_LONG:
- cJSON_AddNumberToObject(_handle->object, key, (long)value);
- break;
- case TLD_TYPE_FILE:
- break;
- case TLD_TYPE_STRING:
- cJSON_AddStringToObject(_handle->object, key, (char *)value);
- break;
- case TLD_TYPE_CJSON:
- cJSON_AddItemToObject(_handle->object, key, (cJSON *)value);
- break;
- default:
- return -1;
- break;
+ cJSON_AddNumberToObject(vlan_array, _instance->id2field[id].name, ntohs(vlan_addr[i].VID));
}
-
- return 0;
+ cJSON_AddItemToObject(tunnel_object, _instance->id2field[id].name, vlan_array);
+
+ return 1;
}
-struct TLD_handle_t *TLD_create(int thread_id)
+static int set_mpls(struct tsg_log_instance_t *_instance, struct single_layer_mpls_addr *mpls_addr, int layer_num, cJSON *tunnel_object, tsg_log_field_id_t id)
{
- //struct _tld_handle *_handle=(struct _tld_handle *)dictator_malloc(thread_id, sizeof(struct _tld_handle));
-
- struct TLD_handle_t *_handle=(struct TLD_handle_t *)calloc(1, sizeof(struct TLD_handle_t));
- _handle->thread_id = thread_id;
- _handle->object = cJSON_CreateObject();
+ if(layer_num==0)
+ {
+ return 0;
+ }
+
+ int i=0;
+ cJSON *mpls_array=cJSON_CreateArray();
+ for(i=0; i<layer_num; i++)
+ {
+ cJSON_AddNumberToObject(mpls_array, _instance->id2field[id].name, ntohl(mpls_addr[i].label));
+ }
- return _handle;
+ cJSON_AddItemToObject(tunnel_object, _instance->id2field[id].name, mpls_array);
+
+ return 1;
}
-static int set_l7_protocol(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
-{
- char *l7_protocol=NULL;
- struct basic_proto_label *l7_proto_label=NULL;
+static int mac_to_string(unsigned char *mac, char *buff)
+{
+ int i=0,len=0;
- l7_proto_label=(struct basic_proto_label *)project_req_get_struct(a_stream, g_tsg_para.l7_proto_project_id);
- if(l7_proto_label!=NULL && l7_proto_label->proto_id!=g_tsg_para.mail_proto_id)
+ for(i=0; i<6; i++)
{
- l7_protocol=tsg_l7_protocol_id2name(_instance, l7_proto_label->proto_id);
- if(l7_protocol!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_L7_PROTOCOL].name, (void *)l7_protocol, TLD_TYPE_STRING);
- return 1;
- }
+ len+=sprintf(buff+len, "%02x:", mac[i]);
}
+ buff[len-1]='\0';
+
return 0;
}
@@ -244,10 +435,10 @@ static int set_link_mac(struct tsg_log_instance_t *_instance, struct layer_addr_
if((memcmp(mac->src_addr.h_source, default_mac, 6)))
{
mac_object=cJSON_CreateObject();
- convert_mac_to_string(mac->src_addr.h_source, buff);
+ mac_to_string(mac->src_addr.h_source, buff);
cJSON_AddStringToObject(mac_object, _instance->id2field[LOG_COMMON_TUNNELS_MAC_SOURCE].name, buff);
- convert_mac_to_string(mac->src_addr.h_dest, buff);
+ mac_to_string(mac->src_addr.h_dest, buff);
cJSON_AddStringToObject(mac_object, _instance->id2field[LOG_COMMON_TUNNELS_MAC_DEST].name, buff);
cJSON_AddItemToObject(tunnel_object, "c2s_direction_mac", mac_object);
@@ -256,10 +447,10 @@ static int set_link_mac(struct tsg_log_instance_t *_instance, struct layer_addr_
if((memcmp(mac->dst_addr.h_source, default_mac, 6)))
{
mac_object=cJSON_CreateObject();
- convert_mac_to_string(mac->dst_addr.h_source, buff);
+ mac_to_string(mac->dst_addr.h_source, buff);
cJSON_AddStringToObject(mac_object, _instance->id2field[LOG_COMMON_TUNNELS_MAC_SOURCE].name, buff);
- convert_mac_to_string(mac->dst_addr.h_dest, buff);
+ mac_to_string(mac->dst_addr.h_dest, buff);
cJSON_AddStringToObject(mac_object, _instance->id2field[LOG_COMMON_TUNNELS_MAC_DEST].name, buff);
cJSON_AddItemToObject(tunnel_object, "s2c_direction_mac", mac_object);
@@ -268,45 +459,102 @@ static int set_link_mac(struct tsg_log_instance_t *_instance, struct layer_addr_
return 1;
}
-
-static int set_vlan(struct tsg_log_instance_t *_instance, struct single_layer_vlan_addr *vlan_addr, int layer_num, cJSON *tunnel_object, tsg_log_field_id_t id)
+static int action2fs_id(int action)
{
- if(layer_num==0)
+ switch(action)
{
- return 0;
+ case TSG_ACTION_DENY:
+ return TSG_FS2_ABORT_DENY;
+ break;
+ case TSG_ACTION_BYPASS:
+ return TSG_FS2_ABORT_ALLOW;
+ break;
+ case TSG_ACTION_MONITOR:
+ return TSG_FS2_ABORT_MONITOR;
+ break;
+ case TSG_ACTION_INTERCEPT:
+ return TSG_FS2_ABORT_INTERCEPT;
+ break;
+ default:
+ return TSG_FS2_ABORT_UNKNOWN;
+ break;
}
+
+ return TSG_FS2_ABORT_UNKNOWN;
+}
+
+int TLD_cancel(struct TLD_handle_t *handle)
+{
+ struct TLD_handle_t *_handle=handle;
- int i=0;
- cJSON *vlan_array=cJSON_CreateArray();
- for(i=0; i<layer_num; i++)
+ if(_handle!=NULL)
{
- cJSON_AddNumberToObject(vlan_array, _instance->id2field[id].name, ntohs(vlan_addr[i].VID));
+ if(_handle->object!=NULL)
+ {
+ cJSON_Delete(_handle->object);
+ _handle->object=NULL;
+ }
+
+ free(handle);
+ handle=NULL;
}
-
- cJSON_AddItemToObject(tunnel_object, _instance->id2field[id].name, vlan_array);
-
- return 1;
+
+ return 0;
}
-static int set_mpls(struct tsg_log_instance_t *_instance, struct single_layer_mpls_addr *mpls_addr, int layer_num, cJSON *tunnel_object, tsg_log_field_id_t id)
+int TLD_delete(struct TLD_handle_t *handle, char *key)
{
- if(layer_num==0)
+ struct TLD_handle_t *_handle=handle;
+
+ if(_handle!=NULL && key!=NULL)
{
- return 0;
+ cJSON_DeleteItemFromObject(_handle->object, key);
}
- int i=0;
- cJSON *mpls_array=cJSON_CreateArray();
- for(i=0; i<layer_num; i++)
+ return 0;
+}
+
+int TLD_append(struct TLD_handle_t *handle, char *key, void *value, TLD_TYPE type)
+{
+ struct TLD_handle_t *_handle=handle;
+
+ if(_handle==NULL || key==NULL || (value==NULL && type!=TLD_TYPE_LONG))
{
- cJSON_AddNumberToObject(mpls_array, _instance->id2field[id].name, ntohl(mpls_addr[i].label));
+ return -1;
}
- cJSON_AddItemToObject(tunnel_object, _instance->id2field[id].name, mpls_array);
+ switch(type)
+ {
+ case TLD_TYPE_LONG:
+ cJSON_AddNumberToObject(_handle->object, key, (long)value);
+ break;
+ case TLD_TYPE_FILE:
+ break;
+ case TLD_TYPE_STRING:
+ cJSON_AddStringToObject(_handle->object, key, (char *)value);
+ break;
+ case TLD_TYPE_CJSON:
+ cJSON_AddItemToObject(_handle->object, key, (cJSON *)value);
+ break;
+ default:
+ return -1;
+ break;
+ }
- return 1;
+
+ return 0;
}
+struct TLD_handle_t *TLD_create(int thread_id)
+{
+ //struct _tld_handle *_handle=(struct _tld_handle *)dictator_malloc(thread_id, sizeof(struct _tld_handle));
+
+ struct TLD_handle_t *_handle=(struct TLD_handle_t *)calloc(1, sizeof(struct TLD_handle_t));
+ _handle->thread_id = thread_id;
+ _handle->object = cJSON_CreateObject();
+
+ return _handle;
+}
static int get_gtp_ipv4v6_port(struct tsg_log_instance_t *_instance, struct streaminfo *a_stream, cJSON *object)
{
@@ -345,7 +593,7 @@ static int get_gtp_ipv4v6_port(struct tsg_log_instance_t *_instance, struct stre
return 0;
}
-static int get_common_tunnels(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
+static int set_common_tunnels(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
int ret=0;
char ip_buff[64]={0};
@@ -458,16 +706,36 @@ char *log_field_id2name(struct tsg_log_instance_t *instance, tsg_log_field_id_t
return NULL;
}
-char *tsg_l7_protocol_id2name(struct tsg_log_instance_t *instance, unsigned short id)
+unsigned long long tsg_get_stream_id(struct streaminfo * a_stream)
{
- struct tsg_log_instance_t *_instance=instance;
+ int ret=0;
+ int device_id_size=sizeof(unsigned long long);
+ unsigned long long device_id=(unsigned long long)g_tsg_para.device_id;
+
+ ret=MESA_get_stream_opt(a_stream, MSO_GLOBAL_STREAM_ID, (void *)&device_id, &device_id_size);
+ if(ret==0)
+ {
+ return device_id;
+ }
+
+ return -1;
+}
+
+int is_multi_hit_same_policy(struct Maat_rule_t *result, int *policy_id, int *policy_id_num)
+{
+ int j=0;
- if(_instance!=NULL && id>=MIN_L7_PROTO_ID && id<=MAX_L7_PROTO_ID)
+ for(j=0;j<*policy_id_num;j++)
{
- return _instance->l7_proto_id2field[id].name;
+ if(policy_id[j]==result->config_id)
+ {
+ return 1;
+ }
}
- return NULL;
+ policy_id[(*policy_id_num)++]=result->config_id;
+
+ return 0;
}
static int set_common_sub_action(struct TLD_handle_t *handle, char *field_name, struct Maat_rule_t *p_result)
@@ -505,33 +773,15 @@ static int set_common_sub_action(struct TLD_handle_t *handle, char *field_name,
return 0;
}
-int set_common_field_from_label(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
+int set_session_attributes(struct tsg_log_instance_t *_instance, struct TLD_handle_t *_handle, struct streaminfo *a_stream)
{
char buff[1024]={0};
- int l7_protocol_flag=0;
- char *l7_protocol=NULL;
- struct app_id_label *app_label=NULL;
struct _location_info_t *location=NULL;
struct _session_attribute_label_t *attribute_label=NULL;
- l7_protocol_flag=set_l7_protocol(_instance, _handle, a_stream);
-
attribute_label=(struct _session_attribute_label_t *)project_req_get_struct(a_stream, _instance->internal_project_id);
if(attribute_label!=NULL)
- {
- if(l7_protocol_flag==0)
- {
- l7_protocol=tsg_schema_index2string(attribute_label->proto);
- if(l7_protocol!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_L7_PROTOCOL].name, (void *)l7_protocol, TLD_TYPE_STRING);
- }
- else
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_L7_PROTOCOL].name, (void *)"UNCATEGORIZED", TLD_TYPE_STRING);
- }
- }
-
+ {
TLD_append(_handle, _instance->id2field[LOG_COMMON_ESTABLISH_LATENCY_MS].name, (void *)attribute_label->establish_latency_ms, TLD_TYPE_LONG);
if(attribute_label->client_asn!=NULL)
@@ -565,159 +815,37 @@ int set_common_field_from_label(struct tsg_log_instance_t *_instance, struct TLD
TLD_append(_handle, _instance->id2field[LOG_SSL_JA3_FINGERPRINT].name, (void *)attribute_label->ja3_fingerprint, TLD_TYPE_STRING);
}
}
- else
- {
- if(l7_protocol_flag==0)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_L7_PROTOCOL].name, (void *)"UNCATEGORIZED", TLD_TYPE_STRING);
- }
- }
-
- app_label=(struct app_id_label *)project_req_get_struct(a_stream, g_tsg_para.app_id_project_id);
- if(app_label!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_APP_ID].name, (void *)(long)app_label->app_id, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_APP_SURROGATE_ID].name, (void *)(long)app_label->surrogate_id, TLD_TYPE_LONG);
- }
return 0;
}
int TLD_append_streaminfo(struct tsg_log_instance_t *instance, struct TLD_handle_t *handle, struct streaminfo *a_stream)
{
- int i_or_e=0,direction=0;
- int ret=0,addr_type=0;
- unsigned short tunnel_type=0;
- char nest_addr_buf[1024];
char *addr_proto=NULL;
- struct timespec tv;
- unsigned int client_isn=0,server_isn=0;
- int size=sizeof(unsigned long long);
- long common_con_duration_ms=0;
- unsigned long long create_time=0;
unsigned long long stream_id=0;
- unsigned short c_port=0, s_port=0;
- int tunnel_type_size=sizeof(tunnel_type);
- struct layer_addr_ipv4 *ipv4=NULL;
- struct layer_addr_ipv6 *ipv6=NULL;
- char server_ip[MAX_IPV4_LEN*8]={0};
- char client_ip[MAX_IPV4_LEN*8]={0};
- struct tcp_flow_stat *tflow_project=NULL;
- struct udp_flow_stat *uflow_project=NULL;
-
struct TLD_handle_t *_handle=handle;
struct tsg_log_instance_t *_instance=instance;
if(_instance==NULL || _handle==NULL || a_stream==NULL)
{
- MESA_handle_runtime_log(_instance->logger,
- RLOG_LV_DEBUG,
- "TLD_APPEND_STREAM",
- "instance==NULL || TLD_handle==NULL || addr==NULL"
- );
+ MESA_handle_runtime_log(_instance->logger, RLOG_LV_DEBUG, "TLD_APPEND_STREAM", "instance==NULL || TLD_handle==NULL || addr==NULL");
return -1;
}
- switch(a_stream->addr.addrtype)
- {
- case ADDR_TYPE_IPV4:
- case __ADDR_TYPE_IP_PAIR_V4:
- ipv4=a_stream->addr.ipv4;
- addr_type=4;
- c_port=ntohs(ipv4->source);
- s_port=ntohs(ipv4->dest);
-
- inet_ntop(AF_INET, (void *)&ipv4->saddr, client_ip, sizeof(client_ip));
- inet_ntop(AF_INET, (void *)&ipv4->daddr, server_ip, sizeof(server_ip));
- break;
- case ADDR_TYPE_IPV6:
- case __ADDR_TYPE_IP_PAIR_V6:
- ipv6=a_stream->addr.ipv6;
- addr_type=6;
- c_port=ntohs(ipv6->source);
- s_port=ntohs(ipv6->dest);
-
- inet_ntop(AF_INET6, (void *)ipv6->saddr, client_ip, sizeof(client_ip));
- inet_ntop(AF_INET6, (void *)ipv6->daddr, server_ip, sizeof(server_ip));
- break;
- default:
- break;
- }
-
-
- TLD_append(_handle, _instance->id2field[LOG_COMMON_SERVER_IP].name, (void *)server_ip, TLD_TYPE_STRING);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_CLIENT_IP].name, (void *)client_ip, TLD_TYPE_STRING);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_SERVER_PORT].name, (void *)(long)s_port, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_CLIENT_PORT].name, (void *)(long)c_port, TLD_TYPE_LONG);
-
- TLD_append(_handle, _instance->id2field[LOG_COMMON_STREAM_DIR].name, (void *)(long)a_stream->dir, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_ADDRESS_TYPE].name, (void *)(long)addr_type, TLD_TYPE_LONG);
-
- switch(a_stream->type)
- {
- case STREAM_TYPE_TCP:
- tflow_project=(struct tcp_flow_stat *)project_req_get_struct(a_stream, _instance->tcp_flow_project_id);
- if(tflow_project!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_PKT_NUM].name, (void *)(long)tflow_project->S2C_all_pkt, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_BYTE_NUM].name, (void *)(long)tflow_project->S2C_all_byte_raw, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_PKT_NUM].name, (void *)(long)tflow_project->C2S_all_pkt, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_BYTE_NUM].name, (void *)(long)tflow_project->C2S_all_byte_raw, TLD_TYPE_LONG);
- }
-
- size=sizeof(unsigned int);
- ret=MESA_get_stream_opt(a_stream, MSO_TCP_ISN_C2S, &client_isn, &size);
- if(ret==0)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_TCP_CLIENT_ISN].name, (void *)(long)client_isn, TLD_TYPE_LONG);
- }
-
- size=sizeof(unsigned int);
- ret=MESA_get_stream_opt(a_stream, MSO_TCP_ISN_S2C, &server_isn, &size);
- if(ret==0)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_TCP_SERVER_ISN].name, (void *)(long)server_isn, TLD_TYPE_LONG);
- }
- break;
- case STREAM_TYPE_UDP:
- uflow_project=(struct udp_flow_stat *)project_req_get_struct(a_stream, _instance->udp_flow_project_id);
- if(uflow_project!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_PKT_NUM].name, (void *)(long)uflow_project->S2C_pkt, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_S2C_BYTE_NUM].name, (void *)(long)uflow_project->S2C_all_byte_raw, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_PKT_NUM].name, (void *)(long)uflow_project->C2S_pkt, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_C2S_BYTE_NUM].name, (void *)(long)uflow_project->C2S_all_byte_raw, TLD_TYPE_LONG);
- }
- break;
- default:
- break;
- }
-
- if(a_stream!=NULL && a_stream->ptcpdetail!=NULL)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_START_TIME].name, (void *)(a_stream->ptcpdetail->createtime), TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_END_TIME].name, (void *)(a_stream->ptcpdetail->lastmtime), TLD_TYPE_LONG);
+ set_app_id(_instance, _handle, a_stream);
+ set_tcp_isn(_instance, _handle, a_stream);
+ set_tuple4(_instance, _handle, a_stream);
+ set_direction(_instance, _handle, a_stream);
+ set_address_list(_instance, _handle, a_stream);
+ set_duraction(_instance, _handle, a_stream);
+ set_packet_bytes(_instance, _handle, a_stream);
+ set_session_attributes(_instance, _handle, a_stream);
- ret=MESA_get_stream_opt(a_stream, MSO_STREAM_CREATE_TIMESTAMP_MS, (void *)&create_time, &size);
- if(ret>=0)
- {
- clock_gettime(CLOCK_REALTIME, &tv);
- common_con_duration_ms=tv.tv_sec*1000+tv.tv_nsec/1000/1000 - create_time;
- }
-
- if(common_con_duration_ms>0)
- {
- TLD_append(_handle, _instance->id2field[LOG_COMMON_CON_DURATION_MS].name, (void *)(common_con_duration_ms), TLD_TYPE_LONG);
- }
- }
- else
+ if(is_tunnels(a_stream))
{
- time_t cur_time=time(NULL);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_START_TIME].name, (void *)cur_time, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_END_TIME].name, (void *)cur_time, TLD_TYPE_LONG);
- TLD_append(_handle, _instance->id2field[LOG_COMMON_CON_DURATION_MS].name, (void *)(common_con_duration_ms), TLD_TYPE_LONG);
+ set_common_tunnels(_instance, _handle, a_stream);
}
-
+
stream_id=tsg_get_stream_id(a_stream);
char stream_id_buff[128]="";
snprintf(stream_id_buff, sizeof(stream_id_buff), "%llu", stream_id);
@@ -725,56 +853,6 @@ int TLD_append_streaminfo(struct tsg_log_instance_t *instance, struct TLD_handle
addr_proto=(char *)layer_addr_prefix_ntop(a_stream);
TLD_append(_handle, _instance->id2field[LOG_COMMON_L4_PROTOCOL].name, (void *)addr_proto, TLD_TYPE_STRING);
-
-
- ret=MESA_get_stream_opt(a_stream, MSO_STREAM_TUNNEL_TYPE, &tunnel_type, &tunnel_type_size);
- assert(ret==0);
- if(tunnel_type==STREAM_TUNNLE_NON)
- {
- layer_addr_ntop_r(a_stream,nest_addr_buf, sizeof(nest_addr_buf));
- }
- else
- {
- stream_addr_list_ntop(a_stream,nest_addr_buf, sizeof(nest_addr_buf));
- }
-
- if(is_tunnels(a_stream))
- {
- get_common_tunnels(_instance, _handle, a_stream);
- }
-
- TLD_append(_handle, _instance->id2field[LOG_COMMON_ADDRESS_LIST].name, (void *)nest_addr_buf, TLD_TYPE_STRING);
-
- set_common_field_from_label(_instance, _handle, a_stream);
-
- i_or_e=MESA_dir_link_to_human(a_stream->routedir);
- switch(a_stream->curdir)
- {
- case DIR_C2S:
- if(i_or_e=='E' || i_or_e=='e')
- {
- direction='E';
- }
- else
- {
- direction='I';
- }
- break;
- case DIR_S2C:
- if(i_or_e=='E' || i_or_e=='e')
- {
- direction='I';
- }
- else
- {
- direction='E';
- }
- break;
- default:
- break;
- }
-
- TLD_append(_handle, _instance->id2field[LOG_COMMON_DIRECTION].name, (void *)(long)direction, TLD_TYPE_LONG);
return 0;
}
@@ -874,7 +952,6 @@ int load_log_common_field(const char *filename, id2field_t *id2field, id2field_t
struct tsg_log_instance_t *tsg_sendlog_init(const char *conffile)
{
int i=0,ret=0;
- int tmp_value=0;
char nic_name[32]={0};
char kafka_errstr[1024]={0};
unsigned int local_ip_nr=0;
@@ -952,9 +1029,6 @@ struct tsg_log_instance_t *tsg_sendlog_init(const char *conffile)
}
inet_ntop(AF_INET,&(local_ip_nr),_instance->local_ip_str,sizeof(_instance->local_ip_str));
- MESA_load_profile_string_def(conffile, "TSG_LOG", "L7_PROTO_ID_FILE", _instance->l7_proto_id_file, sizeof(_instance->l7_proto_id_file), "./tsgconf/app_l7_proto_id.conf");
- load_log_common_field(_instance->l7_proto_id_file, _instance->l7_proto_id2field, NULL, &tmp_value);
-
rdkafka_conf = rd_kafka_conf_new();
rd_kafka_conf_set(rdkafka_conf, "queue.buffering.max.messages", _instance->send_queue_max_msg, kafka_errstr, sizeof(kafka_errstr));
rd_kafka_conf_set(rdkafka_conf, "topic.metadata.refresh.interval.ms", _instance->refresh_interval_ms, kafka_errstr, sizeof(kafka_errstr));
diff --git a/src/tsg_send_log_internal.h b/src/tsg_send_log_internal.h
index 326d2ef..d077abe 100644
--- a/src/tsg_send_log_internal.h
+++ b/src/tsg_send_log_internal.h
@@ -100,7 +100,7 @@ typedef enum _tsg_log_field_id
typedef struct _id2field
{
- TLD_TYPE type;
+ int type;
int id;
char name[MAX_STRING_LEN];
}id2field_t;
@@ -137,7 +137,6 @@ struct tsg_log_instance_t
id2field_t id2field[LOG_COMMON_MAX];
rd_kafka_topic_t **topic_rkt;
id2field_t *service2topic;
- id2field_t l7_proto_id2field[MAX_L7_PROTO_ID+1];
void *logger;
};
diff --git a/src/uthash.h b/src/uthash.h
new file mode 100644
index 0000000..5e5866a
--- /dev/null
+++ b/src/uthash.h
@@ -0,0 +1,1150 @@
+/*
+Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/
+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.
+
+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 UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.1.0
+
+#include <string.h> /* memcmp, memset, strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <stdlib.h> /* exit */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+#ifndef uthash_bzero
+#define uthash_bzero(a,n) memset(a,'\0',n)
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+
+#ifdef uthash_memcmp
+/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */
+#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead"
+#else
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef HASH_KEYCMP
+#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+#ifndef HASH_NONFATAL_OOM
+#define HASH_NONFATAL_OOM 0
+#endif
+
+#if HASH_NONFATAL_OOM
+/* malloc failures can be recovered from */
+
+#ifndef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
+#define IF_HASH_NONFATAL_OOM(x) x
+
+#else
+/* malloc failures result in lost memory, hash tables are unusable */
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
+#define IF_HASH_NONFATAL_OOM(x)
+
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \
+do { \
+ struct UT_hash_handle *_hd_hh_item = (itemptrhh); \
+ unsigned _hd_bkt; \
+ HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ (head)->hh.tbl->buckets[_hd_bkt].count++; \
+ _hd_hh_item->hh_next = NULL; \
+ _hd_hh_item->hh_prev = NULL; \
+} while (0)
+
+#define HASH_VALUE(keyptr,keylen,hashv) \
+do { \
+ HASH_FCN(keyptr, keylen, hashv); \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_bkt; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+ } \
+ } \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_hashv; \
+ HASH_VALUE(keyptr, keylen, _hf_hashv); \
+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
+ } \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl,oomed) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!(tbl)->bloom_bv) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+ } \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl,oomed)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head,oomed) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \
+ if (!(head)->hh.tbl) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+ if (!(head)->hh.tbl->buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } else { \
+ uthash_bzero((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } \
+ ) \
+ } \
+ } \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add) \
+do { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail->next = (add); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ do { \
+ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \
+ break; \
+ } \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ char *_hs_saved_head = (char*)(head); \
+ do { \
+ DECLTYPE_ASSIGN(head, _hs_iter); \
+ if (cmpfcn(head, add) > 0) { \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ break; \
+ } \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+#endif
+
+#if HASH_NONFATAL_OOM
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ if (!(oomed)) { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ if (oomed) { \
+ HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \
+ HASH_DELETE_HH(hh, head, &(add)->hh); \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } else { \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ } \
+ } else { \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } \
+} while (0)
+
+#else
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+} while (0)
+
+#endif
+
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ void *_hs_iter = (head); \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \
+ if (_hs_iter) { \
+ (add)->hh.next = _hs_iter; \
+ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \
+ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \
+ } else { \
+ (head) = (add); \
+ } \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \
+ } else { \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
+do { \
+ unsigned _hs_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1U)); \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+ HASH_DELETE_HH(hh, head, &(delptr)->hh)
+
+#define HASH_DELETE_HH(hh,head,delptrhh) \
+do { \
+ struct UT_hash_handle *_hd_hh_del = (delptrhh); \
+ if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ if (_hd_hh_del == (head)->hh.tbl->tail) { \
+ (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \
+ } \
+ if (_hd_hh_del->prev != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \
+ } else { \
+ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \
+ } \
+ if (_hd_hh_del->next != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh, head, "HASH_DELETE_HH"); \
+} while (0)
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+do { \
+ unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \
+ HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \
+} while (0)
+#define HASH_ADD_STR(head,strfield,add) \
+do { \
+ unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \
+} while (0)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+do { \
+ unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \
+} while (0)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#include <stdio.h> /* fprintf, stderr */
+#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head,where) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count = 0; \
+ char *_prev; \
+ for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \
+ (where), (void*)_thh->hh_prev, (void*)_prev); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \
+ (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev != (char*)_thh->prev) { \
+ HASH_OOPS("%s: invalid prev %p, actual %p\n", \
+ (where), (void*)_thh->prev, (void*)_prev); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid app item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head,where)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv) \
+do { \
+ unsigned _hb_keylen = (unsigned)keylen; \
+ const unsigned char *_hb_key = (const unsigned char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen-- != 0U) { \
+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
+ } \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv) \
+do { \
+ unsigned _sx_i; \
+ const unsigned char *_hs_key = (const unsigned char*)(key); \
+ hashv = 0; \
+ for (_sx_i=0; _sx_i < keylen; _sx_i++) { \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ } \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv) \
+do { \
+ unsigned _fn_i; \
+ const unsigned char *_hf_key = (const unsigned char*)(key); \
+ (hashv) = 2166136261U; \
+ for (_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619U; \
+ } \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv) \
+do { \
+ unsigned _ho_i; \
+ const unsigned char *_ho_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeefu; \
+ _hj_i = _hj_j = 0x9e3779b9u; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12U) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12U; \
+ } \
+ hashv += (unsigned)(keylen); \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
+ \
+ unsigned _sfh_rem = _sfh_len & 3U; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabeu; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0U; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2U*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+} while (0)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
+do { \
+ if ((head).hh_head != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ while ((out) != NULL) { \
+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
+ if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \
+ break; \
+ } \
+ } \
+ if ((out)->hh.hh_next != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ } \
+} while (0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \
+do { \
+ UT_hash_bucket *_ha_head = &(head); \
+ _ha_head->count++; \
+ (addhh)->hh_next = _ha_head->hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (_ha_head->hh_head != NULL) { \
+ _ha_head->hh_head->hh_prev = (addhh); \
+ } \
+ _ha_head->hh_head = (addhh); \
+ if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
+ && !(addhh)->tbl->noexpand) { \
+ HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ HASH_DEL_IN_BKT(head,addhh); \
+ } \
+ ) \
+ } \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(head,delhh) \
+do { \
+ UT_hash_bucket *_hd_head = &(head); \
+ _hd_head->count--; \
+ if (_hd_head->hh_head == (delhh)) { \
+ _hd_head->hh_head = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_prev) { \
+ (delhh)->hh_prev->hh_next = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_next) { \
+ (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \
+ } \
+} while (0)
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero(_he_new_buckets, \
+ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ (tbl)->ideal_chain_maxlen = \
+ ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \
+ ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
+ (tbl)->nonideal_items = 0; \
+ for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \
+ _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh != NULL) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[_he_bkt]); \
+ if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \
+ (tbl)->nonideal_items++; \
+ if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \
+ _he_newbkt->expand_mult++; \
+ } \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head != NULL) { \
+ _he_newbkt->hh_head->hh_prev = _he_thh; \
+ } \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ (tbl)->num_buckets *= 2U; \
+ (tbl)->log2_num_buckets++; \
+ (tbl)->buckets = _he_new_buckets; \
+ (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \
+ ((tbl)->ineff_expands+1U) : 0U; \
+ if ((tbl)->ineff_expands > 1U) { \
+ (tbl)->noexpand = 1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+ } \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head != NULL) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping != 0U) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p != NULL) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \
+ _hs_psize++; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ if (_hs_q == NULL) { \
+ break; \
+ } \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \
+ if (_hs_psize == 0U) { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if ((cmpfcn( \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \
+ )) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail != NULL ) { \
+ _hs_tail->next = ((_hs_e != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e != NULL) { \
+ _hs_e->prev = ((_hs_tail != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail != NULL) { \
+ _hs_tail->next = NULL; \
+ } \
+ if (_hs_nmerges <= 1U) { \
+ _hs_looping = 0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2U; \
+ } \
+ HASH_FSCK(hh, head, "HASH_SRT"); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt = NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if ((src) != NULL) { \
+ for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh != NULL; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \
+ _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh != NULL) { \
+ _last_elt_hh->next = _elt; \
+ } \
+ if ((dst) == NULL) { \
+ DECLTYPE_ASSIGN(dst, _elt); \
+ HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ uthash_nonfatal_oom(_elt); \
+ (dst) = NULL; \
+ continue; \
+ } \
+ ) \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
+ (dst)->hh_dst.tbl->num_items++; \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \
+ HASH_DELETE_HH(hh_dst, dst, _dst_hh); \
+ _dst_hh->tbl = NULL; \
+ uthash_nonfatal_oom(_elt); \
+ continue; \
+ } \
+ ) \
+ HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if ((head) != NULL) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head) \
+ (((head) != NULL) ? ( \
+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ sizeof(UT_hash_table) + \
+ (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */