/* Copyright (c) 2010 UNINETT AS * * This file is a part of VoIPADE engine. * * VoIPADE is a free software, You can copy, redistribute or modify this * Program under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * VoIPADE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /* * File: util-detection.c * Author: Gurvinder Singh * * This file is used to detect the anomaly in the VoIP calls. The Hellinger * distance algorithm has been used to calculate the dynamic threshold. * * Reference: - * * Sengar, Hemant and Wang, Haining and Wijesekera, Duminda and Jajodia, Sushil: * Detecting VoIP Floods Using the Hellinger Distance, * IEEE Trans. Parallel Distrib. Syst., 2008. * */ #include #include #include "voipade.h" #include "util-detection.h" #include "util-log.h" #include "util-cdr.h" #include "util-alert.h" #include "util-conf.h" #define DEFAULT_TIME_INTERVAL 10 #define DEFAULT_SENSTIVITY_VALUE 1.2 #define DEFAULT_ADAPTABILITY_VALUE 0.5 #define DEFAULT_INTERNATIONAL_DURATION 2400 #define DEFAULT_MOBILE_DURATION 3600 #define DEFAULT_PREMIUM_DURATION 2400 #define DEFAULT_INTERNATIONAL_FREQUENCY 10 #define DEFAULT_MOBILE_FREQUENCY 15 #define DEFAULT_PREMIUM_FREQUENCY 10 #define DEFAULT_START_TIME 8 #define DEFAULT_END_TIME 16 #define DEFAULT_QUERY_SIZE 500 /* For the variable values check the reference article in the source file */ static float g = 0.125; /* g = 1/pow(2,3) */ static float h = 0.25; /* h = 1/pow(2,2) */ static double senstivity = 0.0; static double adaptability = 0.0; static int interval = 0; static int int_dur_day = 0; static int mob_dur_day = 0; static int prem_dur_day = 0; static int int_dur_night = 0; static int mob_dur_night= 0; static int prem_dur_night = 0; static int int_freq_day = 0; static int mob_freq_day = 0; static int prem_freq_day = 0; static int int_freq_night = 0; static int mob_freq_night= 0; static int prem_freq_night = 0; static int start_time = 0; static int end_time = 0; static Hd hd_detection; static Hd hd_init; static Hd hd_active_calltype; static struct tm current_time = {0,0,0,0,0,0,0,0,0}; static struct tm *latest_run_tm; static time_t complete_time = 0; static time_t latest_run_time = 0; static char *table = NULL; static char *dbname = NULL; static char *last_transaction_ts = NULL; static MYSQL *threshold_conn = NULL; static char *threshold_table = NULL; static char *threshold_db = NULL; static char previous_ts[25]; static char *accountcode = NULL; static char *thresh_restore = NULL; static char *detect_start_ts = NULL; static char *calltype = NULL; static int call_freq = 0; static int call_dur = 0; static int dayflag = -1; extern uint8_t run_mode; extern uint32_t run_state; static int prevday = 0; static int daylimit = 5; extern char *timeZone; char tz[25]; time_t MkGmTime(struct tm *t) { time_t ti = 0; putenv("TZ=GMT"); tzset(); ti = mktime(t); putenv("TZ=GMT-2"); tzset(); return ti; } void SipSetActiveCalltypes(Hd *hd) { int cnt = 0; for (cnt = 0; cnt < MAX_CALLTYPE && (hd_active_calltype.call[cnt].flag & CALLTYPE_ACTIVE); cnt++) { hd->call[cnt].flag = CALLTYPE_ACTIVE; hd->call[cnt].name = hd_active_calltype.call[cnt].name; } } /** * \brief Function to update the timestamp with the detection timestamp. This * is used when we are restoring the engine and the restored timestamp * value is less than compared to the restored timestamp. * * @param curent_tm pointer to the current time stamp value * @param detect_ts pointer to the detection time stamp value */ void SipSetTimeStamp(struct tm *curent_tm, char *detect_ts) { time_t c_tm = 0; time_t d_tm = 0; struct tm det_tm = {0,0,0,0,0,0,0,0,0}; strptime(detect_ts, "%F %H:%M:%S" ,&det_tm); c_tm = MkGmTime(curent_tm); d_tm = MkGmTime(&det_tm); if (d_tm > c_tm) { strncpy(last_transaction_ts, detect_start_ts, strlen(last_transaction_ts)); detect_start_ts = NULL; strptime(last_transaction_ts, "%F %H:%M:%S" ,¤t_time); } } void SipsetinitHD(Hd *hd) { if(hd_active_calltype.call[MOBILE].flag & CALLTYPE_ACTIVE) { hd->call[MOBILE].dur = dayflag ? mob_dur_day : mob_dur_night; hd->call[MOBILE].num = dayflag ? mob_freq_day: mob_freq_night; hd->num_total = hd->call[MOBILE].num; hd->dur_total = hd->call[MOBILE].dur; } if(hd_active_calltype.call[INTERNATIONAL].flag & CALLTYPE_ACTIVE) { hd->call[INTERNATIONAL].dur = dayflag ? int_dur_day : int_dur_night; hd->call[INTERNATIONAL].num = dayflag ? int_freq_day : int_freq_night; hd->num_total += hd->call[INTERNATIONAL].num; hd->dur_total += hd->call[INTERNATIONAL].dur; } if(hd_active_calltype.call[PREMIUM].flag & CALLTYPE_ACTIVE) { hd->call[PREMIUM].dur = dayflag ? prem_dur_day : prem_dur_night; hd->call[PREMIUM].num = dayflag ? prem_freq_day: prem_freq_night; hd->num_total += hd->call[PREMIUM].num; hd->dur_total += hd->call[PREMIUM].dur; } if(hd_active_calltype.call[DOMESTIC].flag & CALLTYPE_ACTIVE) { hd->call[DOMESTIC].dur = dayflag ? mob_dur_day : mob_dur_night; hd->call[DOMESTIC].num = dayflag ? mob_freq_day : mob_freq_night; hd->num_total += hd->call[DOMESTIC].num; hd->dur_total += hd->call[DOMESTIC].dur; } if(hd_active_calltype.call[SERVICE].flag & CALLTYPE_ACTIVE) { hd->call[SERVICE].dur = dayflag ? prem_dur_day : prem_dur_night; hd->call[SERVICE].num = dayflag ? prem_freq_day : prem_freq_night; hd->num_total += hd->call[SERVICE].num; hd->dur_total += hd->call[SERVICE].dur; } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Initializing HD_INIT flag is:" " %"PRIu32" %d\n", dayflag); } /** * \brief Function to calculate the probability of the number of different * calltypes and their duration. The probabality will be calculated * for the given interval, only if the call frequency is higher than * specified number or specified duration * * @param hd pointer to the threshold struct of which probablity will be * calculated */ void SipCalcHDProbabilities(Hd *hd) { uint8_t cnt = 0; if (hd->num_total > call_freq || hd->dur_total > call_dur) { for (cnt = 0; cnt < MAX_CALLTYPE && (hd_active_calltype.call[cnt].flag & CALLTYPE_ACTIVE); cnt++) { hd->call[cnt].p_freq = (double)hd->call[cnt].num/ (double)(hd->num_total + hd->dur_total); SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "prob of Number of Call" " Type %d: %f", cnt, hd->call[cnt].p_freq); hd->call[cnt].p_dur = (double)hd->call[cnt].dur/ (double)(hd->num_total + hd->dur_total); SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "prob of Duration of Call" " Type %d: %f", cnt, hd->call[cnt].p_dur); } } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Total Number of all Calls:" " %"PRIu64" %d\n", hd->num_total, call_freq); SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Duration of Total Calls:" " %"PRIu64"\n", hd->dur_total); } int SipupdateDayflag(int flag) { int switchFlag = 0; strptime(last_transaction_ts, "%F %H:%M:%S" ,¤t_time); if ((current_time.tm_hour >= start_time) && (current_time.tm_hour < end_time) && (current_time.tm_wday != 0 && current_time.tm_wday != 6)) { if (dayflag != 1) { dayflag = 1; switchFlag = 1; CLEAR_HD(&hd_init); SipSetActiveCalltypes(&hd_init); SipsetinitHD(&hd_init); /* Calculate the probablity for each call type */ SipCalcHDProbabilities(&hd_init); } } else { if (dayflag != 0) { dayflag = 0; switchFlag = 1; CLEAR_HD(&hd_init); SipSetActiveCalltypes(&hd_init); SipsetinitHD(&hd_init); /* Calculate the probablity for each call type */ SipCalcHDProbabilities(&hd_init); } } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "last_transaction_ts %s and tm_hour %d dayflag %d", last_transaction_ts, current_time.tm_hour,dayflag); return switchFlag; } void SipupdateThreshold(MYSQL *conn) { SipAnomalyStoreThreshold(); if (SipupdateDayflag(FALSE) == 1) { CLEAR_HD(&hd_detection); SipSetActiveCalltypes(&hd_detection); if(SipAnomalyFecthThreshold() != SIP_THRESHOLD_RESTORED) { SipTrainingInitThreshold(conn); } } } int SipAnomalyFecthThreshold() { char query[DEFAULT_QUERY_SIZE]; char *thresh_id = NULL; uint64_t threshold_id = 0; if (run_state == SIP_STATE_INIT) { snprintf(query, sizeof(query), "select max(threshold_id) from %s.%s where " "accountcode='%s' and run_state='%"PRIu32"'", threshold_db, threshold_table,accountcode, SIP_STATE_DETECTION); } else { snprintf(query, sizeof(query), "select max(threshold_id) from %s.%s where " "accountcode='%s' and dayflag='%"PRIu32"'", threshold_db,threshold_table,accountcode,dayflag); } if (mysql_query(threshold_conn, query)) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making the" " given query \"%s\" dur to %s", query, mysql_error(threshold_conn)); return SIP_THRESHOLD_NOT_RESTORED; } MYSQL_RES *res = mysql_use_result(threshold_conn); MYSQL_ROW mrow = mysql_fetch_row(res); thresh_id = mrow[0]; if (thresh_id == NULL) { SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Threshold is not restored as threshold id is empty"); mysql_free_result(res); return SIP_THRESHOLD_NOT_RESTORED; } threshold_id = strtoul(thresh_id, NULL, 10); mysql_free_result(res); if (threshold_id > 0) { if (last_transaction_ts == NULL) { last_transaction_ts = (char *) calloc(1, (25 * sizeof (char))); if (last_transaction_ts == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "failed in " "allocating memory"); return SIP_THRESHOLD_NOT_RESTORED; } } snprintf(query, sizeof(query), "select * from %s.%s where " "threshold_id='%"PRIu64"'", threshold_db,threshold_table, threshold_id); res = SipGetCdr(threshold_conn, query); if (res == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making " "the given query \"%s\"", query); return SIP_THRESHOLD_NOT_RESTORED; } /* Restore the threshold values from the threshold database with the * last threshold values stored in the database */ char *temp = NULL; uint8_t cnt = 0; uint8_t col_cnt = 1; MYSQL_ROW rrow = mysql_fetch_row(res); for (cnt = 0; cnt < MAX_CALLTYPE; cnt++) { temp = rrow[col_cnt]; hd_detection.call[cnt].num = atoi(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.call[cnt].dur = atoi(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.call[cnt].p_freq = atof(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.call[cnt].p_dur = atof(temp); col_cnt++; } temp = rrow[col_cnt]; hd_detection.num_total = atol(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.dur_total = atol(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.distance_value = atof(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.mean_deviation = atof(temp); col_cnt++; temp = rrow[col_cnt]; hd_detection.threshold = atof(temp); col_cnt++; temp = rrow[col_cnt]; if(run_state == SIP_STATE_INIT) { strncpy(last_transaction_ts, temp, strlen(temp)); //strncpy(previous_ts, last_transaction_ts, 24); /* Initialize the current_time struct, which will be used for interval * advancement */ strptime(last_transaction_ts, "%F %H:%M:%S" ,¤t_time); } if (detect_start_ts != NULL) { SipSetTimeStamp(¤t_time, detect_start_ts); } if(run_state == SIP_STATE_INIT) { hd_detection.flags |= THRESHOLD_RESTORED; } SipupdateDayflag(TRUE); mysql_free_result(res); return SIP_THRESHOLD_RESTORED; } return SIP_THRESHOLD_NOT_RESTORED; } /** * \brief Function to update the timestamp with the given time interval. This * is used in feteching the data from the cdr database. * * @param interval value of the interval which will be added to the timestamp * * @return returns SIP_OK upon success and SIP_DONE upon completion, when * running in offline mode */ int SipUpdateTimeStamp(uint32_t interval) { if (run_mode & SIP_RUN_MODE_OFFLINE) { if (MkGmTime(¤t_time) > complete_time) { SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION, "Returing as the " "anomaly detection done.."); return SIP_DONE; } } current_time.tm_min += interval; MkGmTime(¤t_time); strncpy(previous_ts, last_transaction_ts, 24); strftime(last_transaction_ts, 25, "%F %H:%M:%S", ¤t_time); SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "interval is %d\n",interval); return SIP_OK; } /** * \brief Function to return the timestamp value of the last interval, which * will be used in the logging to the log file * @return returns the pointer to the timestamp value */ char *SipGetTimeStamp() { return previous_ts; } void SipUpdateRunMode() { if (! (run_mode & SIP_RUN_MODE_OFFLINE)) { if (latest_run_time > (MkGmTime(¤t_time)+500)) { run_mode = SIP_RUN_MODE_INTERIM; time_t crunts = time(NULL); crunts += 3600; if(crunts > (latest_run_time+60)) { SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION," Adjusting starting time" " to current time %d starting time %d",crunts,latest_run_time); latest_run_time += (crunts - latest_run_time); } } else { run_mode = SIP_RUN_MODE_ONLINE; SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION," Starting time %d Catch up " "time %d human readable catchup(last_ts) %s",latest_run_time, MkGmTime(¤t_time), last_transaction_ts); latest_run_time = 0; } } } void SipSetCallTypeString() { int i = 0; int siz = 0; calltype = (char *)calloc(1, DEFAULT_CALLTYPE_LEN); char *orig_pos = calltype; while (i < MAX_CALLTYPE) { if (hd_active_calltype.call[i].flag & CALLTYPE_ACTIVE) { siz = strlen(hd_active_calltype.call[i].name); *calltype = '\''; calltype++; memcpy(calltype, hd_active_calltype.call[i].name, siz); calltype += siz; *calltype = '\''; calltype++; *calltype = ','; calltype++; } i++; } calltype--; *calltype = '\0'; calltype = orig_pos; SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Calltype string is %s", calltype); } /** * \brief Function to make the query string for the given interval * * @param query pointer to the query string in which the final string will * be stored * @param timestamp pointer to the timestamp value from which the data will be * feteched * @param interval pointer to the interval vallue which will be added to the * timestamp to fetch the data */ void SipGetQuery(char *query, char *timestamp, int interval, char *direction) { snprintf(query, DEFAULT_QUERY_SIZE, "select id,start,src_username,dst_username,duration," "category,accountcode,direction from %s.%s where end >= '%s'" " and end < DATE_ADD('%s', INTERVAL %d MINUTE) and category in (%s)" " and accountcode='%s' and direction='%s'",dbname,table,timestamp, timestamp,interval,calltype,accountcode,direction); } /** * \brief Function to fetch the data rekated to different calltypes and their * duration. * @param hd pointer to the struct in which the result will be stored * @param result pointer to the result feteched from the cdr database for * given interval */ void SipGetCallData(Hd *hd, MYSQL_RES *result) { char *calltype = NULL; char *billsec = NULL; int i_billsec = 0; uint8_t cnt = 0; MYSQL_ROW mrow; /*row_cnt = mysql_num_rows(result); if (row_cnt == 0) { SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "No calls received, returning"); return; }*/ /* Get the data for various call types */ while ((mrow = mysql_fetch_row(result)) != NULL) { calltype = mrow[5]; /* Get data for INTERNATIONAL call type */ if (strncmp(calltype, "INTERNATIONAL", 13) == 0) { hd->call[INTERNATIONAL].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[INTERNATIONAL].dur += i_billsec; /* Get data for MOBILE call type */ } else if (strncmp(calltype, "MOBILE", 6) == 0) { hd->call[MOBILE].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[MOBILE].dur += i_billsec; /* Get data for PREMIUM call type */ } else if (strncmp(calltype, "PREMIUM", 7) == 0) { hd->call[PREMIUM].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[PREMIUM].dur += i_billsec; /* Get data for DOMESTIC call type */ } else if (strncmp(calltype, "DOMESTIC", 8) == 0) { hd->call[DOMESTIC].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[DOMESTIC].dur += i_billsec; /* Get data for SERVICE call type */ } else if (strncmp(calltype, "SERVICE", 7) == 0) { hd->call[SERVICE].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[SERVICE].dur += i_billsec; /* Get data for EMERGENCY call type */ } else if (strncmp(calltype, "EMERGENCY", 9) == 0) { hd->call[EMERGENCY].num++; billsec = mrow[4]; i_billsec = strtoul(billsec,NULL,10); hd->call[EMERGENCY].dur += i_billsec; } } /* Get the total data of the all the fetched call types */ for (cnt = 0; cnt < MAX_CALLTYPE; cnt++) { /* Get the total number of all the fetched call types */ hd->num_total += hd->call[cnt].num; /* Get the total duration of the all the fetched call types */ hd->dur_total += hd->call[cnt].dur; } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "International calls %d and" " duration %d, Mobile calls %d and duration %d, Premium calls %d and" "duration %d, Domestic calls %d and duration %d, Service calls %d" " and duration %d Emergency calls %d and duration %d Total calls " "%"PRIu64" and duration %"PRIu64", timestamp %s", hd->call[INTERNATIONAL].num, hd->call[INTERNATIONAL].dur, hd->call[MOBILE].num, hd->call[MOBILE].dur, hd->call[PREMIUM].num, hd->call[PREMIUM].dur, hd->call[DOMESTIC].num, hd->call[DOMESTIC].dur, hd->call[SERVICE].num, hd->call[SERVICE].dur, hd->call[EMERGENCY].num, hd->call[EMERGENCY].dur, hd->num_total, hd->dur_total, last_transaction_ts); } /** * \brief Function to calculate the training distance value for the current period * against the given detection struct. This distance value will be used * in calculating the threshold value at later stage. During the * detection period, we are more sensitive to the international and * premium service calls * * @param hd_detection pointer to the detection struct against which distance * value is calculated * @param hd_testing pointer to the values of the current testing period, * which will be used to calculate the distance */ void SipCalcHellingerDistance (Hd *hd_detection, Hd *hd_testing) { double fcall[MAX_CALLTYPE] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double dcall[MAX_CALLTYPE] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };; uint8_t cnt = 0; for (cnt = 0; cnt < MAX_CALLTYPE && (hd_active_calltype.call[cnt].flag & CALLTYPE_ACTIVE); cnt++) { if (hd_testing->call[cnt].p_freq != 0 || hd_detection->distance_value == 0) { fcall[cnt] = (pow((sqrt(hd_detection->call[cnt].p_freq) - sqrt(hd_testing->call[cnt].p_freq)), 2)); } if (hd_testing->call[cnt].p_dur != 0 || hd_detection->distance_value == 0) { dcall[cnt] = (pow((sqrt(hd_detection->call[cnt].p_dur) - sqrt(hd_testing->call[cnt].p_dur)), 2)); } /* calculate the hellinger distance */ hd_testing->distance_value += fcall[cnt] + dcall[cnt]; } /* This case happen only during the initial setup */ if ((hd_testing->distance_value > 0.999999) && run_state < SIP_STATE_DETECTION) { SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION, "Earlier Distance Value %f at time %s", hd_testing->distance_value, last_transaction_ts); if(dayflag == 1) { hd_testing->distance_value = 0.5; } else { hd_testing->distance_value = 0.25; } for (cnt = 0; cnt < MAX_CALLTYPE && (hd_active_calltype.call[cnt].flag & CALLTYPE_ACTIVE); cnt++) { hd_testing->call[cnt].dur = hd_detection->call[cnt].dur; hd_testing->call[cnt].num = hd_detection->call[cnt].num; } SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION, "Updated Distance Value %f at time %s", hd_testing->distance_value, last_transaction_ts); } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "Detection distance Value %f" " Testing distance Value %f", hd_detection->distance_value, hd_testing->distance_value); } /** * \brief Function to update the threshold value from the current calculted * value, so that the engine will adapt to the changes in the behavior * * @param hd_detection poiner the threshold struct, which will be updated * @param hd_testing pointer to the threshold struct from which value will * be updated */ void SipUpdateHDThreshold(Hd *hd_detection, Hd *hd_testing) { double error = 0.0; uint8_t cnt = 0; SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION,"test distance is %f training" " distance is %f and run state %d\n", hd_testing->distance_value, hd_detection->distance_value, run_state); error = hd_testing->distance_value - hd_detection->distance_value; if ((error < adaptability && error > -adaptability) || (hd_detection->distance_value == 0.0)) { SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION,"error is %f\n", error); hd_detection->distance_value = hd_detection->distance_value + (g * error); /* get the absolute value of error */ error = fabs(error); SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION,"new training distance is %f" " old mean %f\n", hd_detection->distance_value, hd_detection->mean_deviation); hd_detection->mean_deviation = hd_detection->mean_deviation + (h*(error - hd_detection->mean_deviation)); hd_detection->threshold = (senstivity * hd_detection->distance_value) + (adaptability * hd_detection->mean_deviation); for (cnt = 0; cnt < MAX_CALLTYPE && (hd_active_calltype.call[cnt].flag & CALLTYPE_ACTIVE); cnt++) { /* Update frequency probability */ if (hd_detection->call[cnt].p_freq > hd_testing->call[cnt].p_freq) { hd_detection->call[cnt].p_freq -= hd_testing->call[cnt].p_freq*adaptability; } else if (hd_detection->call[cnt].p_freq < hd_testing->call[cnt].p_freq) { hd_detection->call[cnt].p_freq += hd_testing->call[cnt].p_freq*adaptability; } if (hd_detection->call[cnt].p_freq <= 0.00001) { hd_detection->call[cnt].p_freq = hd_init.call[cnt].p_freq/2; } else if (hd_detection->call[cnt].p_freq >= 0.99) { hd_detection->call[cnt].p_freq = hd_init.call[cnt].p_freq; } /* Update Duration probability */ if (hd_detection->call[cnt].p_dur > hd_testing->call[cnt].p_dur) { hd_detection->call[cnt].p_dur -= hd_testing->call[cnt].p_dur*adaptability; } else if (hd_detection->call[cnt].p_dur < hd_testing->call[cnt].p_dur) { hd_detection->call[cnt].p_dur += hd_testing->call[cnt].p_dur*adaptability; } if (hd_detection->call[cnt].p_dur <= 0.00001) { hd_detection->call[cnt].p_dur = hd_init.call[cnt].p_dur/2; } else if (hd_detection->call[cnt].p_dur >= 0.99) { hd_detection->call[cnt].p_dur = hd_init.call[cnt].p_dur; } /* Update Number of calls */ if (hd_detection->call[cnt].num > hd_testing->call[cnt].num) { hd_detection->call[cnt].num -= hd_testing->call[cnt].num*adaptability; } else if (hd_detection->call[cnt].num < hd_testing->call[cnt].num) { hd_detection->call[cnt].num += hd_testing->call[cnt].num*adaptability; } if (hd_detection->call[cnt].num < hd_init.call[cnt].num/2) { hd_detection->call[cnt].num = hd_init.call[cnt].num/2; } else if (hd_detection->call[cnt].num > hd_init.call[cnt].num*1.5) { hd_detection->call[cnt].num = hd_init.call[cnt].num; } /* Update duration of calls */ if (hd_detection->call[cnt].dur > hd_testing->call[cnt].dur) { hd_detection->call[cnt].dur -= hd_testing->call[cnt].dur*adaptability; } else if (hd_detection->call[cnt].dur < hd_testing->call[cnt].dur) { hd_detection->call[cnt].dur += hd_testing->call[cnt].dur*adaptability; } if (hd_detection->call[cnt].dur < hd_init.call[cnt].dur/2) { hd_detection->call[cnt].dur = hd_init.call[cnt].dur/2; } else if (hd_detection->call[cnt].num > hd_init.call[cnt].dur*1.5) { hd_detection->call[cnt].dur = hd_init.call[cnt].dur; } } SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "threshold value is %f" " hd_distance %f mean %f error %f", hd_detection->threshold, hd_detection->distance_value, hd_detection->mean_deviation, error); } } /** * \brief Function to print the given threshold struct with all other * parameters to file provided. * * @param hd pointer to the threshold struct to be printed * @param fp pointer to the file descriptor where the value should written * */ void SipPrintHD(Hd *hd, FILE *fp) { fprintf(fp, "\n*****Hellinger Distance (%p) Field values are*****\n",hd); fprintf(fp, "Number of Mobile Calls: %d\n", hd->call[MOBILE].num); fprintf(fp, "Number of International Calls: %d\n", hd->call[INTERNATIONAL].num); fprintf(fp, "Number of Premium Calls: %d\n", hd->call[PREMIUM].num); fprintf(fp, "Number of Domestic Calls: %d\n", hd->call[DOMESTIC].num); fprintf(fp, "Number of Service Calls: %d\n", hd->call[SERVICE].num); fprintf(fp, "Number of Emergency Calls: %d\n", hd->call[EMERGENCY].num); fprintf(fp, "Total Number of all Calls: %"PRIu64"\n", hd->num_total); fprintf(fp, "Prob. of number of mobile calls: %f\n" ,hd->call[MOBILE].p_freq); fprintf(fp, "Prob. of Number of International Calls: %f\n" ,hd->call[INTERNATIONAL].p_freq); fprintf(fp, "Prob. of Number of Premium Calls: %f\n" ,hd->call[PREMIUM].p_freq); fprintf(fp, "Prob. of Number of Domestic Calls: %f\n" ,hd->call[DOMESTIC].p_freq); fprintf(fp, "Prob. of Number of Service Calls: %f\n" ,hd->call[SERVICE].p_freq); fprintf(fp, "Prob. of Number of Emergency Calls: %f\n" ,hd->call[EMERGENCY].p_freq); fprintf(fp, "Duration of Mobile Calls: %d\n", hd->call[MOBILE].dur); fprintf(fp, "Duration of International Calls: %d\n" ,hd->call[INTERNATIONAL].dur); fprintf(fp, "Duration of Premium Calls: %d\n", hd->call[PREMIUM].dur); fprintf(fp, "Duration of Domestic Calls: %d\n", hd->call[DOMESTIC].dur); fprintf(fp, "Duration of Service Calls: %d\n", hd->call[SERVICE].dur); fprintf(fp, "Duration of Emergency Calls: %d\n", hd->call[EMERGENCY].dur); fprintf(fp, "Duration of Total Calls: %"PRIu64"\n", hd->dur_total); fprintf(fp, "Prob. of Duration of mobile calls: %f\n" ,hd->call[MOBILE].p_dur); fprintf(fp, "Prob. of Duration of International Calls: %f\n" ,hd->call[INTERNATIONAL].p_dur); fprintf(fp, "Prob. of Duration of Premium Calls: %f\n" ,hd->call[PREMIUM].p_dur); fprintf(fp, "Prob. of Duration of Domestic Calls: %f\n" ,hd->call[DOMESTIC].p_dur); fprintf(fp, "Prob. of Duration of Service Calls: %f\n" ,hd->call[SERVICE].p_dur); fprintf(fp, "Prob. of Duration of Emergency Calls: %f\n" ,hd->call[EMERGENCY].p_dur); fprintf(fp, "Distance Value %f\n", hd->distance_value); fprintf(fp, "Mean Deviation Value %f\n", hd->mean_deviation); fprintf(fp, "Threshold Value %f\n", hd->threshold); fprintf(fp, "********************************************************\n"); } /** * \brief Function to fetch the config values related to the detection algo * and traning engine. * * @return returns SIP_OK upon success and SIP_ERROR on failure */ int SipAnomalyInitConfValues() { char *senstivity_s = NULL; char *interval_s = NULL; char *adaptability_s = NULL; char *int_dur_day_s = NULL; char *mob_dur_day_s = NULL; char *prem_dur_day_s = NULL; char *int_dur_night_s = NULL; char *mob_dur_night_s = NULL; char *prem_dur_night_s = NULL; char *int_freq_day_s = NULL; char *mob_freq_day_s = NULL; char *prem_freq_day_s = NULL; char *int_freq_night_s = NULL; char *mob_freq_night_s = NULL; char *prem_freq_night_s = NULL; char *start_time_s = NULL; char *end_time_s = NULL; char *call_fs = NULL; char *call_ds = NULL; extern uint8_t run_mode; char *ending_s = NULL; char *calltype_s = NULL; /* Get the table name from the database connection information given in * the configuration file */ if (SipConfGet("cdr-database.table", &table) != 1) { table = calloc(1, sizeof("cdr")); table = "cdr"; } if (SipConfGet("cdr-database.database-name", &dbname) != 1) { dbname = calloc(1, sizeof("sip")); dbname = "sip"; } if (SipConfGet("ad-algo.sensitivity", &senstivity_s) == 1) { senstivity = atof(senstivity_s); } else { senstivity = DEFAULT_SENSTIVITY_VALUE; } if (SipConfGet("ad-algo.adaptability", &adaptability_s) == 1) { adaptability = atof(adaptability_s); } else { adaptability = DEFAULT_ADAPTABILITY_VALUE; } if (SipConfGet("ad-algo.interval", &interval_s) == 1) { interval = atoi(interval_s); } else { interval = DEFAULT_TIME_INTERVAL; } if (SipConfGet("call-duration.day-mobile", &mob_dur_day_s) == 1) { mob_dur_day = atoi(mob_dur_day_s) * 60; } else { mob_dur_day = DEFAULT_MOBILE_DURATION; } if (SipConfGet("call-duration.day-international", &int_dur_day_s) == 1) { int_dur_day = atoi(int_dur_day_s) * 60; } else { int_dur_day = DEFAULT_INTERNATIONAL_DURATION; } if (SipConfGet("call-duration.day-premium", &prem_dur_day_s) == 1) { prem_dur_day = atoi(prem_dur_day_s) * 60; } else { prem_dur_day = DEFAULT_PREMIUM_DURATION; } if (SipConfGet("call-duration.night-mobile", &mob_dur_night_s) == 1) { mob_dur_night = atoi(mob_dur_night_s) * 60; } else { mob_dur_night = DEFAULT_MOBILE_DURATION; } if (SipConfGet("call-duration.night-international", &int_dur_night_s) == 1) { int_dur_night = atoi(int_dur_night_s) * 60; } else { int_dur_night = DEFAULT_INTERNATIONAL_DURATION; } if (SipConfGet("call-duration.night-premium", &prem_dur_night_s) == 1) { prem_dur_night = atoi(prem_dur_night_s) * 60; } else { prem_dur_night = DEFAULT_PREMIUM_DURATION; } if (SipConfGet("call-frequency.day-mobile", &mob_freq_day_s) == 1) { mob_freq_day = atoi(mob_freq_day_s); } else { mob_freq_day = DEFAULT_MOBILE_FREQUENCY; } if (SipConfGet("call-frequency.day-international", &int_freq_day_s) == 1) { int_freq_day = atoi(int_freq_day_s); } else { int_freq_day = DEFAULT_INTERNATIONAL_FREQUENCY; } if (SipConfGet("call-frequency.day-premium", &prem_freq_day_s) == 1) { prem_freq_day = atoi(prem_freq_day_s); } else { prem_freq_day = DEFAULT_PREMIUM_FREQUENCY; } if (SipConfGet("call-frequency.night-mobile", &mob_freq_night_s) == 1) { mob_freq_night = atoi(mob_freq_night_s); } else { mob_freq_night = DEFAULT_MOBILE_FREQUENCY; } if (SipConfGet("call-frequency.night-international", &int_freq_night_s) == 1) { int_freq_night = atoi(int_freq_night_s); } else { int_freq_night = DEFAULT_INTERNATIONAL_FREQUENCY; } if (SipConfGet("call-frequency.night-premium", &prem_freq_night_s) == 1) { prem_freq_night = atoi(prem_freq_night_s); } else { prem_freq_night = DEFAULT_PREMIUM_FREQUENCY; } if (SipConfGet("office-time.start_time", &start_time_s) == 1) { start_time = atoi(start_time_s); //start_time -= 1; /* struct tm has hour values from 0-23 */ } else { start_time = DEFAULT_START_TIME; } if (SipConfGet("office-time.end_time", &end_time_s) == 1) { end_time = atoi(end_time_s); } else { end_time = DEFAULT_END_TIME; } snprintf(tz, 23, "TZ=%s",timeZone); if (SipConfGet("institution", &accountcode) != 1) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Institution code has" " not been provided in the configuration file. Please provide the code" " to start the engine :-)"); return SIP_ERROR; } if (SipConfGet("ad-algo.threshold-restore", &thresh_restore) != 1) { thresh_restore = calloc(1, 4*sizeof(char)); thresh_restore = "yes"; } if (SipConfGet("detection-start-ts", &detect_start_ts) != 1) { detect_start_ts = NULL; } if (SipConfGet("ad-algo.call-freq", &call_fs) == 1) { call_freq = atoi(call_fs); } if (SipConfGet("ad-algo.call-duration", &call_ds) == 1) { call_dur = atoi(call_ds) * 60; } if (SipConfGet("ending-date", &ending_s) == 1) { struct tm ending_time = {0,0,0,0,0,0,0,0,0}; strptime(ending_s, "%F %H:%M:%S" ,&ending_time); complete_time = MkGmTime(&ending_time); } else { if (run_mode & SIP_RUN_MODE_OFFLINE) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "please mention the" " ending time while running in offline mode."); return SIP_ERROR; } } if (SipConfGet("call-type", &calltype_s) == 1) { char *call_t = strtok(calltype_s, ","); while( call_t != NULL ) { /* remove the spaces */ while(isspace(*call_t)) { call_t++; } if (strncasecmp(call_t, "All", 3) == 0) { hd_active_calltype.call[INTERNATIONAL].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[INTERNATIONAL].name = "INTERNATIONAL"; hd_active_calltype.call[MOBILE].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[MOBILE].name = "MOBILE"; hd_active_calltype.call[PREMIUM].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[PREMIUM].name = "PREMIUM"; hd_active_calltype.call[DOMESTIC].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[DOMESTIC].name = "DOMESTIC"; hd_active_calltype.call[EMERGENCY].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[EMERGENCY].name = "EMERGENCY"; hd_active_calltype.call[SERVICE].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[SERVICE].name = "SERVICE"; break; } else if (strncasecmp(call_t, "International", 13) == 0) { hd_active_calltype.call[INTERNATIONAL].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[INTERNATIONAL].name = "INTERNATIONAL"; } else if (strncasecmp(call_t, "Mobile", 6) == 0) { hd_active_calltype.call[MOBILE].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[MOBILE].name = "MOBILE"; } else if (strncasecmp(call_t, "Premium", 7) == 0) { hd_active_calltype.call[PREMIUM].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[PREMIUM].name = "PREMIUM"; } else if (strncasecmp(call_t, "Domestic", 8) == 0) { hd_active_calltype.call[DOMESTIC].flag |= CALLTYPE_ACTIVE; printf("domestic calltype is active\n"); hd_active_calltype.call[DOMESTIC].name = "DOMESTIC"; } else if (strncasecmp(call_t, "Emergency", 9) == 0) { hd_active_calltype.call[EMERGENCY].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[EMERGENCY].name = "EMERGENCY"; } else if (strncasecmp(call_t, "Service", 7) == 0) { hd_active_calltype.call[SERVICE].flag |= CALLTYPE_ACTIVE; hd_active_calltype.call[SERVICE].name = "SERVICE"; } call_t = strtok(NULL, ","); } SipSetCallTypeString(); } else { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "please mention atleast one " "calltype for which you want to run the detection engine."); return SIP_ERROR; } return SIP_OK; } /** * \brief Function to initialize the detection modeule. It tries to restore * the threshold value from the stored threshold values, if restoration * has been enabled in the config file. * * @return SIP_THRESHOLD_NOT_RESTORE if threshold has not been restored and * SIP_THRESHOLD_RESTORE if threshold has been restored. Or return * SIP_ERROR if an error has occured */ int SipInitAnomalyDetection() { CLEAR_HD(&hd_detection); time_t runts = time(NULL); latest_run_tm = localtime(&runts); latest_run_time = MkGmTime(latest_run_tm); /* Get the default values of configuration parameter from config file */ if (SipAnomalyInitConfValues() != SIP_OK) return SIP_ERROR; if (SipConfGet("threshold-database.database-name", &threshold_db) != 1) { threshold_db = calloc(1, sizeof("sip")); threshold_db = "sip"; } /* connect to the data base with the provided connection information */ threshold_conn = SipConnectDB("threshold-database"); if(!threshold_conn) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in " "connecting to threshold-database"); return SIP_ERROR; } if (SipConfGet("threshold-database.table", &threshold_table) != 1) { threshold_table = calloc(1, sizeof("threshold")); threshold_table = "threshold"; } char *ts = NULL; if (SipConfGet("initial-timestamp", &ts) == 1) { last_transaction_ts = strdup(ts); SipupdateDayflag(TRUE); } if (strncmp(thresh_restore, "no", 2) == 0) { return SIP_THRESHOLD_NOT_RESTORED; } SipSetActiveCalltypes(&hd_detection); return SipAnomalyFecthThreshold(); } /** * \brief Function to store the current threshold value in the threshold databse * which will be used for restoring the detection engine upon failure or * restart. * * @return returns SIP_OK upon success and SIP_ERROR on failure */ int SipAnomalyStoreThreshold() { char query[1000]; snprintf(query, sizeof (query), "insert into %s(num_int,dur_int,p_fint," "p_dint,num_mob,dur_mob,p_fmob,p_dmob,num_prem,dur_prem,p_fprem," "p_dprem,num_ser,dur_ser,p_fser,p_dser,num_dom,dur_dom,p_fdom," "p_ddom,num_emr,dur_emr,p_femr,p_demr,num_total,dur_total," "dist_value, mean_dev,threshold, last_ts, accountcode, dayflag,run_state) " "values ('%"PRIu32"','%"PRIu32"','%f','%f'," "'%"PRIu32"','%"PRIu32"','%f','%f','%"PRIu32"','%"PRIu32"','%f','%f'," "'%"PRIu32"','%"PRIu32"','%f','%f','%"PRIu32"','%"PRIu32"','%f','%f'," "'%"PRIu32"','%"PRIu32"','%f','%f','%"PRIu64"','%"PRIu64"','%f','%f'," "'%f','%s', '%s','%"PRIu32"','%"PRIu32"')", threshold_table, hd_detection.call[INTERNATIONAL].num, hd_detection.call[INTERNATIONAL].dur, hd_detection.call[INTERNATIONAL].p_freq, hd_detection.call[INTERNATIONAL].p_dur,hd_detection.call[MOBILE].num, hd_detection.call[MOBILE].dur, hd_detection.call[MOBILE].p_freq, hd_detection.call[MOBILE].p_dur,hd_detection.call[PREMIUM].num, hd_detection.call[PREMIUM].dur, hd_detection.call[PREMIUM].p_freq, hd_detection.call[PREMIUM].p_dur,hd_detection.call[SERVICE].num, hd_detection.call[SERVICE].dur, hd_detection.call[SERVICE].p_freq, hd_detection.call[SERVICE].p_dur,hd_detection.call[DOMESTIC].num, hd_detection.call[DOMESTIC].dur, hd_detection.call[DOMESTIC].p_freq, hd_detection.call[DOMESTIC].p_dur,hd_detection.call[EMERGENCY].num, hd_detection.call[EMERGENCY].dur, hd_detection.call[EMERGENCY].p_freq, hd_detection.call[EMERGENCY].p_dur,hd_detection.num_total, hd_detection.dur_total, hd_detection.distance_value, hd_detection.mean_deviation, hd_detection.threshold, last_transaction_ts, accountcode, dayflag,run_state); if (mysql_query(threshold_conn, query)) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in inserting" " the given values \"%s\"", query); } return SIP_OK; } /** * \brief Function to initialize the threshold value. It fetches the first * two cdr records for the given time interval and initialize the engine * * @param conn Pointer to the CDR database * @return returns SIP_OK upon success and SIP_ERROR on failure */ int SipTrainingInitThreshold(MYSQL *conn) { MYSQL_RES *result = NULL; char query[DEFAULT_QUERY_SIZE]; SipSetActiveCalltypes(&hd_init); SipSetActiveCalltypes(&hd_detection); if (last_transaction_ts == NULL) { snprintf(query, DEFAULT_QUERY_SIZE, "select unix_timestamp(" "start) from %s.%s order by id limit 1", dbname, table); last_transaction_ts = (char *) calloc(1, (25 * sizeof (char))); if (last_transaction_ts == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "failed in " "allocating memory"); return SIP_ERROR; } /* Fetch the initial timestamp data from the cdr database with the given * query */ result = SipGetCdr(conn, query); if (result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making " "the given query \"%s\"", query); return SIP_OK; } MYSQL_ROW trow = mysql_fetch_row(result); strncpy(last_transaction_ts, trow[0], strlen(trow[0])); /* Convert to timetsamp value */ strptime(last_transaction_ts, "%s" ,¤t_time); SipUpdateTimeStamp(0); SipupdateThreshold(conn); mysql_free_result(result); } else { strptime(last_transaction_ts, "%F %H:%M:%S" ,¤t_time); } /* Initialize the initial hellinger distance value //printf("ts is %s\n", last_transaction_ts); //SipGetQuery(query, last_transaction_ts, interval); //printf("%s\n",query); result = SipGetCdr(conn, query); if (result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making the" " given query \"%s\"", query); return SIP_OK; }*/ /* Get different call type data */ //SipGetCallData(&hd_train_init, result); //mysql_free_result(result); //SipUpdateTimeStamp(interval); SipGetQuery(query, last_transaction_ts, interval, "OUTBOUND"); result = SipGetCdr(conn, query); if (result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making the" " given query \"%s\"", query); return SIP_OK; } /* Get different call type data */ SipGetCallData(&hd_detection, result); mysql_free_result(result); /* Calculate the probablity for each call type */ SipCalcHDProbabilities(&hd_detection); /* Calculate the initial hellinger distance value to be stored in * hd_detection */ SipCalcHellingerDistance(&hd_init, &hd_detection); /* Initialize the threshold values*/ SipUpdateHDThreshold(&hd_detection, &hd_init); /* Update the timestamp to fetch date for next time interval. The increment * is equal to 10 minutes */ SipUpdateTimeStamp(interval); SipupdateThreshold(conn); SipAnomalyStoreThreshold(); return SIP_OK; } /** * \brief Function to train the detection module. It trains the anomaly * detection algorithm over the training dataset and initializes the * threshold value. * * @param conn Pointer to the CDR database * @return returns SIP_OK upon success and SIP_ERROR on failure */ int SipTrainingAnomalyDetection(MYSQL *conn) { MYSQL_RES *result = NULL; Hd hd_train; char query[DEFAULT_QUERY_SIZE]; /* Fetch the required data from the cdr database with the given query for * next interval */ SipGetQuery(query, last_transaction_ts, interval, "OUTBOUND"); //printf("%s\n",query); result = SipGetCdr(conn, query); if (result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making the" " given query \"%s\"", query); return SIP_OK; } CLEAR_HD(&hd_train); /* Get different call type data */ SipGetCallData(&hd_train, result); mysql_free_result(result); /* Calculate the probablity for each call type */ SipCalcHDProbabilities(&hd_train); /* Calculate the initial hellinger distance value to be stored in * hd_detection */ SipCalcHellingerDistance(&hd_detection, &hd_train); /* Initialize the threshold values*/ if (hd_train.distance_value > 0) { SipUpdateHDThreshold(&hd_detection, &hd_train); SipAnomalyStoreThreshold(); } /* Update the timestamp to fetch date for next time interval. The increment * is equal to 10 minutes */ SipUpdateTimeStamp(interval); SipupdateThreshold(conn); //SipPrintHD(&hd_train, stdout); return SIP_OK; } /** * \brief Function to detect the anomaly using the trained hellinger * distance algorithm over the testing period. After initialization * new threshold value will be calculated which will reflect the * current traffic bahavior of the institute. * * @param conn Pointer to the CDR database * @param result pointer to the result which will contains the call data * * @return returns TRUE upon anomaly detection and FALSE upon normal behavior */ int SipAnomalyDetection(MYSQL *conn, MYSQL_RES **result, char *direction) { Hd hd_testing; int ret_value = SIP_OK; char query[DEFAULT_QUERY_SIZE]; /* Delete any stored threshold of older than one week*/ if(prevday != current_time.tm_mday) { char tquery[DEFAULT_QUERY_SIZE]; snprintf(tquery, sizeof(tquery),"DELETE from %s.%s WHERE last_ts <= " "DATE_SUB('%s', INTERVAL %d DAY) and accountcode='%s'",threshold_db, threshold_table,last_transaction_ts,daylimit,accountcode); *result = SipGetCdr(threshold_conn, tquery); mysql_free_result(*result); prevday = current_time.tm_mday; } /* Initialize the timestamp to start detection from the given detection * start time in the config file */ if (detect_start_ts != NULL && !(hd_detection.flags & THRESHOLD_RESTORED) && run_state == SIP_STATE_DETECTION) { strncpy(last_transaction_ts, detect_start_ts, strlen(last_transaction_ts)); detect_start_ts = NULL; strptime(last_transaction_ts, "%F %H:%M:%S" ,¤t_time); SipupdateThreshold(conn); /* Check if the detect timestamp is also old or not, if it is old then run in offline mode for a while until we reach the current timestamp snprintf(query, DEFAULT_QUERY_SIZE, "select start from %s.%s order by" " start DESC limit 1", dbname, table);*/ /* Fetch the initial timestamp data from the cdr database with the given * query *result = SipGetCdr(conn, query); if (*result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making " "the given query \"%s\"", query); return SIP_OK; } MYSQL_ROW trow = mysql_fetch_row(*result); strptime(trow[0], "%F %H:%M:%S" ,&latest_det_time); latest_run_time = MkGmTime(&latest_det_time); mysql_free_result(*result);*/ } if (latest_run_time > 0 && (strcmp(direction, "OUTBOUND") == 0)) { SipUpdateRunMode(); /* As we have changed the run mode, we will wait now data to come before we make further query to retreive cdr records */ if(latest_run_time == 0) { return SIP_NODATA; } } /* Fetch the required data from the cdr database with the given query for * next interval */ SipGetQuery(query, last_transaction_ts, interval, direction); //SipLog(SIP_LOG_INFO, SIP_LOG_LOCATION, "query is: %s", query); *result = SipGetCdr(conn, query); if (*result == NULL) { SipLog(SIP_LOG_ERROR, SIP_LOG_LOCATION, "Failed in making the" " given query \"%s\"", query); return SIP_OK; } CLEAR_HD(&hd_testing); /* Get different call type data */ SipGetCallData(&hd_testing, *result); if (hd_testing.num_total == 0) { SipLog(SIP_LOG_DEBUG, SIP_LOG_LOCATION, "No calls made in this interval," " thus returning"); if ((strcmp(direction, "OUTBOUND") == 0) && SipUpdateTimeStamp(interval) == SIP_DONE) return SIP_DONE; SipupdateThreshold(conn); return SIP_NODATA; } /* Calculate the probablity for each call type */ SipCalcHDProbabilities(&hd_testing); /* Calculate the initial hellinger distance value to be stored in * hd_detection */ SipCalcHellingerDistance(&hd_detection, &hd_testing); if (hd_testing.distance_value > hd_detection.threshold) { if (dayflag == 1) { if(strcmp(direction, "OUTBOUND") == 0) { if (hd_testing.call[MOBILE].dur > mob_dur_day || (hd_testing.call[INTERNATIONAL].dur > int_dur_day) || (hd_testing.call[PREMIUM].dur > prem_dur_day) || ((hd_testing.call[INTERNATIONAL].num > senstivity*hd_detection.call[INTERNATIONAL].num) && (hd_detection.call[INTERNATIONAL].num > 0))) { ret_value = SIP_ALERT; } else if ((hd_testing.call[DOMESTIC].dur > mob_dur_day) || (hd_testing.call[SERVICE].dur > prem_dur_day) || (hd_testing.call[EMERGENCY].num > prem_freq_day)) { ret_value = SIP_ALERT; } } else { if (hd_testing.call[INTERNATIONAL].num > int_freq_day*1.5) ret_value = SIP_ALERT; } } else { if (hd_testing.call[MOBILE].dur > mob_dur_night || (hd_testing.call[INTERNATIONAL].dur > int_dur_night) || (hd_testing.call[PREMIUM].dur > prem_dur_night) || (hd_testing.call[MOBILE].num > mob_freq_night) || (hd_testing.call[INTERNATIONAL].num > int_freq_night) || (hd_testing.call[PREMIUM].num > prem_freq_night)) { ret_value = SIP_ALERT; } else if ((hd_testing.call[DOMESTIC].dur > mob_dur_night) || (hd_testing.call[SERVICE].dur > prem_dur_night) || (hd_testing.call[EMERGENCY].num > prem_freq_night)) { ret_value = SIP_ALERT; } } } else if (hd_testing.distance_value > 0 && (strcmp(direction, "OUTBOUND") == 0)) { SipUpdateHDThreshold(&hd_detection, &hd_testing); } if (ret_value == SIP_ALERT) { mysql_free_result(*result); *result = SipGetCdr(conn, query); } /* Update the timestamp to fetch date for next time interval. The increment * is equal to given interval minutes */ if ((strcmp(direction, "OUTBOUND") == 0) && SipUpdateTimeStamp(interval) == SIP_DONE) { if (ret_value == SIP_ALERT) { SipAlertNotification(SIP_STATUS_ALERT, result); } SipupdateThreshold(conn); return SIP_DONE; } SipupdateThreshold(conn); return ret_value; } /** * \brief Function to clear the memory and close the connection to threshold * database, while shutting down the engine. */ void SipDeinitAnomalyDetection() { if (last_transaction_ts != NULL) { free (last_transaction_ts); } if (threshold_conn != NULL) { mysql_close(threshold_conn); } if (calltype != NULL) { free (calltype); } }