/* * Copyright (C) 2019 iopsys Software Solutions AB * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation * * Author: Amin Ben Ramdhane * */ #include #include #include #ifdef LOPENSSL #include #endif #ifdef LWOLFSSL #include #include #endif #ifdef LMBEDTLS #include #include #include #endif #include "dmentry.h" #include "dmdiagnostics.h" #define READ_BUF_SIZE (1024 * 16) static int read_next; struct diagnostic_stats diag_stats = {0}; char *get_diagnostics_option(char *sec_name, char *option) { char *value; dmuci_get_option_value_string_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, option, &value); return value; } char *get_diagnostics_option_fallback_def(char *sec_name, char *option, char *default_value) { char *value = get_diagnostics_option(sec_name, option); return (*value != '\0') ? value : default_value; } void set_diagnostics_option(char *sec_name, char *option, char *value) { check_create_dmmap_package(DMMAP_DIAGNOSTIGS); struct uci_section *section = dmuci_walk_section_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, NULL, NULL, CMP_SECTION, NULL, NULL, GET_FIRST_SECTION); if (!section) dmuci_set_value_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, "", sec_name); dmuci_set_value_bbfdm(DMMAP_DIAGNOSTIGS, sec_name, option, value); } void reset_diagnostic_state(char *sec_name) { char *diag_state = get_diagnostics_option(sec_name, "DiagnosticState"); if (strcmp(diag_state, "Requested") != 0) { set_diagnostics_option(sec_name, "DiagnosticState", "None"); } } char *get_diagnostics_interface_option(struct dmctx *ctx, char *value) { char *linker = NULL; if (!value || *value == 0) return ""; if (strncmp(value, "Device.IP.Interface.", 20) != 0) return ""; adm_entry_get_linker_value(ctx, value, &linker); return linker ? linker : ""; } void set_diagnostics_interface_option(struct dmctx *ctx, char *sec_name, char *value) { char *linker = NULL; if (!value || *value == 0) return; if (strncmp(value, "Device.IP.Interface.", 20) != 0) return; adm_entry_get_linker_value(ctx, value, &linker); set_diagnostics_option(sec_name, "interface", linker ? linker : ""); } static bool get_response_code_status(const char *url, int response_code) { if ((strncmp(url, HTTP_URI, strlen(HTTP_URI)) == 0 && response_code != 200) || (strncmp(url, FTP_URI, strlen(FTP_URI)) == 0 && response_code != 226) || (strncmp(url, FILE_URI, strlen(FILE_URI)) == 0 && response_code != 0) || (strncmp(url, HTTP_URI, strlen(HTTP_URI)) && strncmp(url, FTP_URI, strlen(FTP_URI)) && strncmp(url, FILE_URI, strlen(FILE_URI)))) { return false; } return true; } static void send_transfer_complete_event(const char *command, const char *obj_path, const char *transfer_url, long res_code, time_t start_t, time_t complete_t,const char *commandKey, const char *transfer_type) { char start_time[32] = {0}; char complete_time[32] = {0}; char fault_string[128] = {0}; unsigned fault_code = 0; strftime(start_time, sizeof(start_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&start_t)); strftime(complete_time, sizeof(complete_time), "%Y-%m-%dT%H:%M:%SZ", gmtime(&complete_t)); if (!get_response_code_status(transfer_url, res_code)) { fault_code = USP_FAULT_GENERAL_FAILURE; snprintf(fault_string, sizeof(fault_string), "%s operation is failed, fault code (%ld)", transfer_type, res_code); } struct json_object *obj = json_object_new_object(); json_object_object_add(obj, "Command", json_object_new_string(command)); if(commandKey) json_object_object_add(obj, "CommandKey", json_object_new_string(commandKey)); else json_object_object_add(obj, "CommandKey", json_object_new_string("")); json_object_object_add(obj, "Requestor", json_object_new_string("")); json_object_object_add(obj, "TransferType", json_object_new_string(transfer_type)); json_object_object_add(obj, "Affected", json_object_new_string(obj_path)); json_object_object_add(obj, "TransferURL", json_object_new_string(transfer_url)); json_object_object_add(obj, "StartTime", json_object_new_string(start_time)); json_object_object_add(obj, "CompleteTime", json_object_new_string(complete_time)); json_object_object_add(obj, "FaultCode", json_object_new_uint64(fault_code)); json_object_object_add(obj, "FaultString", json_object_new_string(fault_string)); dmubus_call_set("usp.raw", "notify_event", UBUS_ARGS{{"name", "Device.LocalAgent.TransferComplete!", String}, {"input", json_object_to_json_string(obj), Table}}, 2); json_object_put(obj); } static long download_file(char *file_path, const char *url, const char *username, const char *password) { long res_code = 0; if (strncmp(url, FILE_URI, strlen(FILE_URI)) == 0) { const char *curr_path = (!strncmp(url, FILE_LOCALHOST_URI, strlen(FILE_LOCALHOST_URI))) ? url + strlen(FILE_LOCALHOST_URI) : url + strlen(FILE_URI); if (!file_exists(curr_path)) return -1; DM_STRNCPY(file_path, curr_path, 256); } else { CURL *curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_USERNAME, username); curl_easy_setopt(curl, CURLOPT_PASSWORD, password); curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); FILE *fp = fopen(file_path, "wb"); if (fp) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_perform(curl); fclose(fp); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); curl_easy_cleanup(curl); } } return res_code; } static long upload_file(const char *file_path, const char *url, const char *username, const char *password) { long res_code = 0; CURL *curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_USERNAME, username); curl_easy_setopt(curl, CURLOPT_PASSWORD, password); curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); FILE *fp = fopen(file_path, "rb"); if (fp) { curl_easy_setopt(curl, CURLOPT_READDATA, fp); curl_easy_perform(curl); fclose(fp); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); curl_easy_cleanup(curl); } return res_code; } const bool validate_file_system_size(const char *file_size) { if (file_size && *file_size) { unsigned long f_size = strtoul(file_size, NULL, 10); unsigned long fs_available_size = file_system_size("/tmp", FS_SIZE_AVAILABLE); if (fs_available_size < f_size) return false; } return true; } #if defined(LOPENSSL) || defined(LWOLFSSL) #define SHA1_DLEN SHA_DIGEST_LENGTH #define SHA1_CTX SHA_CTX #define SHA1_UPDATE SHA1_Update #define SHA1_FINAL SHA1_Final #else #define SHA1_DLEN (20) #define SHA1_CTX mbedtls_sha1_context #define SHA1_UPDATE mbedtls_sha1_update_ret #define SHA1_FINAL(X, Y) mbedtls_sha1_finish_ret(Y, X) #endif const bool validate_sha1sum_value(const char *file_path, const char *checksum) { unsigned char hash[SHA1_DLEN]; unsigned char buffer[READ_BUF_SIZE]; char sha1_res[1 + SHA1_DLEN * 2]; bool res = false; int bytes = 0; SHA1_CTX ctx; FILE *file = fopen(file_path, "rb"); if (!file) return false; #if defined(LOPENSSL) || defined(LWOLFSSL) if (!SHA1_Init(&ctx)) goto end; #else mbedtls_sha1_init(&ctx); #endif while ((bytes = fread (buffer, 1, sizeof(buffer), file))) { if (!SHA1_UPDATE(&ctx, buffer, bytes)) goto end; } if (!SHA1_FINAL(hash, &ctx)) goto end; for (int i = 0; i < SHA1_DLEN; i++) snprintf(&sha1_res[i * 2], sizeof(sha1_res) - (i * 2), "%02x", hash[i]); if (DM_STRCMP(sha1_res, checksum) == 0) res = true; end: fclose(file); return res; } #if defined(LOPENSSL) #define SHA224_DLEN SHA256_DIGEST_LENGTH #define SHA224_CTX_t SHA256_CTX #define SHA224_INIT SHA224_Init #define SHA224_UPDATE SHA224_Update #define SHA224_FINAL SHA224_Final #elif defined(LWOLFSSL) #define SHA224_DLEN SHA256_DIGEST_LENGTH #define SHA224_CTX_t SHA256_CTX #define SHA224_INIT SHA256_Init #define SHA224_UPDATE SHA256_Update #define SHA224_FINAL SHA256_Final #else #define SHA224_DLEN (32) #define SHA224_CTX_t mbedtls_sha256_context #define SHA224_UPDATE mbedtls_sha256_update_ret #define SHA224_FINAL(X, Y) mbedtls_sha256_finish_ret(Y, X) #endif const bool validate_sha224sum_value(const char *file_path, const char *checksum) { unsigned char hash[SHA224_DLEN]; unsigned char buffer[READ_BUF_SIZE]; char sha224_res[1 + SHA224_DLEN * 2]; bool res = false; int bytes = 0; SHA224_CTX_t ctx; FILE *file = fopen(file_path, "rb"); if (!file) return false; #if defined(LOPENSSL) || defined(LWOLFSSL) if (!SHA224_INIT(&ctx)) goto end; #else mbedtls_sha256_init(&ctx); if (!mbedtls_sha256_starts_ret(&ctx, 1)) goto end; #endif while ((bytes = fread (buffer, 1, sizeof(buffer), file))) { if (!SHA224_UPDATE(&ctx, buffer, bytes)) goto end; } if (!SHA224_FINAL(hash, &ctx)) goto end; for (int i = 0; i < SHA224_DLEN; i++) snprintf(&sha224_res[i * 2], sizeof(sha224_res) - (i * 2), "%02x", hash[i]); if (DM_STRCMP(sha224_res, checksum) == 0) res = true; end: fclose(file); return res; } #if defined(LOPENSSL) || defined(LWOLFSSL) #define SHA256_DLEN SHA256_DIGEST_LENGTH #define SHA256_CTX_t SHA256_CTX #define SHA256_UPDATE SHA256_Update #define SHA256_FINAL SHA256_Final #else #define SHA256_DLEN (32) #define SHA256_CTX_t mbedtls_sha256_context #define SHA256_UPDATE mbedtls_sha256_update_ret #define SHA256_FINAL(X, Y) mbedtls_sha256_finish_ret(Y, X) #endif const bool validate_sha256sum_value(const char *file_path, const char *checksum) { unsigned char hash[SHA256_DLEN]; unsigned char buffer[READ_BUF_SIZE]; char sha256_res[1 + SHA256_DLEN * 2]; bool res = false; int bytes = 0; SHA256_CTX_t ctx; FILE *file = fopen(file_path, "rb"); if (!file) return false; #if defined(LOPENSSL) || defined(LWOLFSSL) if (!SHA256_Init(&ctx)) goto end; #else mbedtls_sha256_init(&ctx); if (!mbedtls_sha256_starts_ret(&ctx, 0)) goto end; #endif while ((bytes = fread (buffer, 1, sizeof(buffer), file))) { if (!SHA256_UPDATE(&ctx, buffer, bytes)) goto end; } if (!SHA256_FINAL(hash, &ctx)) goto end; for (int i = 0; i < SHA256_DLEN; i++) snprintf(&sha256_res[i * 2], sizeof(sha256_res) - (i * 2), "%02x", hash[i]); if (DM_STRCMP(sha256_res, checksum) == 0) res = true; end: fclose(file); return res; } #if defined(LOPENSSL) #define SHA384_DLEN SHA384_DIGEST_LENGTH #define SHA384_CTX_t SHA512_CTX #define SHA384_UPDATE SHA384_Update #define SHA384_FINAL SHA384_Final #elif defined(LWOLFSSL) #define SHA384_DLEN SHA384_DIGEST_LENGTH #define SHA384_CTX_t SHA384_CTX #define SHA384_UPDATE SHA384_Update #define SHA384_FINAL SHA384_Final #else #define SHA384_DLEN (64) #define SHA384_CTX_t mbedtls_sha512_context #define SHA384_UPDATE mbedtls_sha512_update_ret #define SHA384_FINAL(X, Y) mbedtls_sha512_finish_ret(Y, X) #endif const bool validate_sha384sum_value(const char *file_path, const char *checksum) { unsigned char hash[SHA384_DLEN]; unsigned char buffer[READ_BUF_SIZE]; char sha384_res[1 + SHA384_DLEN * 2]; bool res = false; int bytes = 0; SHA384_CTX_t ctx; FILE *file = fopen(file_path, "rb"); if (!file) return false; #if defined(LOPENSSL) || defined(LWOLFSSL) if (!SHA384_Init(&ctx)) goto end; #else mbedtls_sha512_init(&ctx); if (!mbedtls_sha512_starts_ret(&ctx, 1)) goto end; #endif while ((bytes = fread (buffer, 1, sizeof(buffer), file))) { if (!SHA384_UPDATE(&ctx, buffer, bytes)) goto end; } if (!SHA384_FINAL(hash, &ctx)) goto end; for (int i = 0; i < SHA384_DLEN; i++) snprintf(&sha384_res[i * 2], sizeof(sha384_res) - (i * 2), "%02x", hash[i]); if (DM_STRCMP(sha384_res, checksum) == 0) res = true; end: fclose(file); return res; } #if defined(LOPENSSL) || defined(LWOLFSSL) #define SHA512_DLEN SHA512_DIGEST_LENGTH #define SHA512_CTX_t SHA512_CTX #define SHA512_UPDATE SHA512_Update #define SHA512_FINAL SHA512_Final #else #define SHA512_DLEN (64) #define SHA512_CTX_t mbedtls_sha512_context #define SHA512_UPDATE mbedtls_sha512_update_ret #define SHA512_FINAL(X, Y) mbedtls_sha512_finish_ret(Y, X) #endif const bool validate_sha512sum_value(const char *file_path, const char *checksum) { unsigned char hash[SHA512_DLEN]; unsigned char buffer[READ_BUF_SIZE]; char sha512_res[1 + SHA512_DLEN * 2]; bool res = false; int bytes = 0; SHA512_CTX_t ctx; FILE *file = fopen(file_path, "rb"); if (!file) return false; #if defined(LOPENSSL) || defined(LWOLFSSL) if (!SHA512_Init(&ctx)) goto end; #else mbedtls_sha512_init(&ctx); if (!mbedtls_sha512_starts_ret(&ctx, 0)) goto end; #endif while ((bytes = fread (buffer, 1, sizeof(buffer), file))) { if (!SHA512_UPDATE(&ctx, buffer, bytes)) goto end; } if (!SHA512_FINAL(hash, &ctx)) goto end; for (int i = 0; i < SHA512_DLEN; i++) snprintf(&sha512_res[i * 2], sizeof(sha512_res) - (i * 2), "%02x", hash[i]); if (DM_STRCMP(sha512_res, checksum) == 0) res = true; end: fclose(file); return res; } const bool validate_checksum_value(const char *file_path, const char *checksum_algorithm, const char *checksum) { if (checksum && *checksum) { if (strcmp(checksum_algorithm, "SHA-1") == 0) return validate_sha1sum_value(file_path, checksum); else if (strcmp(checksum_algorithm, "SHA-224") == 0) return validate_sha224sum_value(file_path, checksum); else if (strcmp(checksum_algorithm, "SHA-256") == 0) return validate_sha256sum_value(file_path, checksum); else if (strcmp(checksum_algorithm, "SHA-384") == 0) return validate_sha384sum_value(file_path, checksum); else if (strcmp(checksum_algorithm, "SHA-512") == 0) return validate_sha512sum_value(file_path, checksum); else return false; } return true; } int bbf_config_backup(const char *url, const char *username, const char *password, char *config_name, const char *command, const char *obj_path) { int res = 0; // Export config file to backup file if (dmuci_export_package(config_name, CONFIG_BACKUP)) { res = -1; goto end; } // Upload the config file time_t start_time = time(NULL); long res_code = upload_file(CONFIG_BACKUP, url, username, password); time_t complete_time = time(NULL); // Send Transfer Complete Event send_transfer_complete_event(command, obj_path, url, res_code, start_time, complete_time,NULL,"Upload"); // Check if the upload operation was successful if (!get_response_code_status(url, res_code)) { res = -1; goto end; } end: // Remove temporary file if (file_exists(CONFIG_BACKUP) && remove(CONFIG_BACKUP)) res = -1; return res; } int bbf_upload_log(const char *url, const char *username, const char *password, char *config_name, const char *command, const char *obj_path) { int res = 0; // Upload the config file time_t start_time = time(NULL); long res_code = upload_file(config_name, url, username, password); time_t complete_time = time(NULL); // Send Transfer Complete Event send_transfer_complete_event(command, obj_path, url, res_code, start_time, complete_time,NULL, "Upload"); // Check if the upload operation was successful if (!get_response_code_status(url, res_code)) { res = -1; } return res; } int bbf_config_restore(const char *url, const char *username, const char *password, const char *file_size, const char *checksum_algorithm, const char *checksum, const char *command, const char *obj_path) { char config_restore[256] = "/tmp/bbf_config_restore"; int res = 0; // Check the file system size if there is sufficient space for downloading the config file if (!validate_file_system_size(file_size)) { res = -1; goto end; } // Download the firmware image time_t start_time = time(NULL); long res_code = download_file(config_restore, url, username, password); time_t complete_time = time(NULL); // Send Transfer Complete Event send_transfer_complete_event(command, obj_path, url, res_code, start_time, complete_time, NULL, "Download"); // Check if the download operation was successful if (!get_response_code_status(url, res_code)) { res = -1; goto end; } // Validate the CheckSum value according to its algorithm if (!validate_checksum_value(config_restore, checksum_algorithm, checksum)) { res = -1; goto end; } // Apply config file if (dmuci_import(NULL, config_restore)) res = -1; end: // Remove temporary file if (file_exists(config_restore) && strncmp(url, FILE_URI, strlen(FILE_URI)) && remove(config_restore)) res = -1; return res; } int bbf_fw_image_download(const char *url, const char *auto_activate, const char *username, const char *password, const char *file_size, const char *checksum_algorithm, const char *checksum, const char *bank_id, const char *command, const char *obj_path, const char *commandKey) { char fw_image_path[256] = "/tmp/firmware-XXXXXX"; json_object *json_obj = NULL; bool activate = false; int res = 0; // Check the file system size if there is sufficient space for downloading the firmware image if (!validate_file_system_size(file_size)) { res = -1; goto end; } res = mkstemp(fw_image_path); if (res == -1) { goto end; } else { close(res); // close the fd, as only filename required res = 0; } // Download the firmware image time_t start_time = time(NULL); long res_code = download_file(fw_image_path, url, username, password); time_t complete_time = time(NULL); // Send Transfer Complete Event send_transfer_complete_event(command, obj_path, url, res_code, start_time, complete_time,commandKey, "Download"); // Check if the download operation was successful if (!get_response_code_status(url, res_code)) { res = -1; goto end; } // Validate the CheckSum value according to its algorithm if (!validate_checksum_value(fw_image_path, checksum_algorithm, checksum)) { res = -1; goto end; } string_to_bool((char *)auto_activate, &activate); char *act = (activate) ? "1" : "0"; // Apply Firmware Image dmubus_call_blocking("fwbank", "upgrade", UBUS_ARGS{{"path", fw_image_path, String}, {"auto_activate", act, Boolean}, {"bank", bank_id, Integer}}, 3, &json_obj); if (json_obj == NULL) { res = -1; goto end; } sleep(30); // Wait for the image to become available // Reboot the device if auto activation is true if (activate) { if (dmubus_call_set("system", "reboot", UBUS_ARGS{0}, 0) != 0) res = -1; sleep(10); // Wait for reboot to take action } end: // Remove temporary file if ubus upgrade failed and file exists if (!json_obj && file_exists(fw_image_path) && strncmp(url, FILE_URI, strlen(FILE_URI))) { remove(fw_image_path); res = -1; } if (json_obj != NULL) json_object_put(json_obj); return res; } static void libtrace_cleanup(libtrace_t *trace, libtrace_packet_t *packet) { if (trace) trace_destroy(trace); if (packet) trace_destroy_packet(packet); } static void set_stats_value(char *diag_type) { char buf[16] = {0}; set_diagnostics_option(diag_type, "ROMtime", ((diag_stats.romtime)[0] != 0) ? diag_stats.romtime : "0001-01-01T00:00:00.000000Z"); set_diagnostics_option(diag_type, "BOMtime", ((diag_stats.bomtime)[0] != 0) ? diag_stats.bomtime : "0001-01-01T00:00:00.000000Z"); set_diagnostics_option(diag_type, "EOMtime", ((diag_stats.eomtime)[0] != 0) ? diag_stats.eomtime : "0001-01-01T00:00:00.000000Z"); set_diagnostics_option(diag_type, "TCPOpenRequestTime", ((diag_stats.tcpopenrequesttime)[0] != 0) ? diag_stats.tcpopenrequesttime : "0001-01-01T00:00:00.000000Z"); set_diagnostics_option(diag_type, "TCPOpenResponseTime",((diag_stats.tcpopenresponsetime)[0] != 0) ? diag_stats.tcpopenresponsetime : "0001-01-01T00:00:00.000000Z"); snprintf(buf, sizeof(buf), "%d", diag_stats.test_bytes_received); set_diagnostics_option(diag_type, "TestBytesReceived", buf); } static int get_tcp_flag_from_packet(libtrace_packet_t *packet, libtrace_tcp_t **tcp, char *tcp_flag, char **nexthdr) { uint8_t proto; uint32_t remaining; *tcp = trace_get_transport(packet, &proto, &remaining); if (*tcp == NULL) return -1; *nexthdr = trace_get_payload_from_tcp(*tcp, &remaining); if ((*tcp)->ecn_ns) strcat(tcp_flag, "ECN_NS "); if ((*tcp)->cwr) strcat(tcp_flag, "CWR "); if ((*tcp)->ece) strcat(tcp_flag, "ECE "); if ((*tcp)->fin) strcat(tcp_flag, "FIN "); if ((*tcp)->syn) strcat(tcp_flag, "SYN "); if ((*tcp)->rst) strcat(tcp_flag, "RST "); if ((*tcp)->psh) strcat(tcp_flag, "PSH "); if ((*tcp)->ack) strcat(tcp_flag, "ACK "); if ((*tcp)->urg) strcat(tcp_flag, "URG "); return 0; } static void http_download_per_packet(libtrace_packet_t *packet) { libtrace_tcp_t *tcp = NULL; char *nexthdr, tcp_flag[16] = {0}, s_now[20] = {0}; struct tm http_download_lt; if (get_tcp_flag_from_packet(packet, &tcp, tcp_flag, &nexthdr)) return; struct timeval http_download_ts = trace_get_timeval(packet); gmtime_r(&(http_download_ts.tv_sec), &http_download_lt); strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &http_download_lt); if (strcmp(tcp_flag, "SYN ") == 0 && diag_stats.random_seq == 0) { snprintf(diag_stats.tcpopenrequesttime, sizeof(diag_stats.tcpopenrequesttime), "%s.%06ldZ", s_now, (long) http_download_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "SYN ACK ") == 0 && diag_stats.random_seq != 0 && (ntohl(tcp->ack_seq) - 1 ) == diag_stats.random_seq) { snprintf(diag_stats.tcpopenresponsetime, sizeof(diag_stats.tcpopenresponsetime), "%s.%06ldZ", s_now, (long) http_download_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && strncmp(nexthdr, "GET", 3) == 0) { snprintf(diag_stats.romtime, sizeof(diag_stats.romtime), "%s.%06ldZ", s_now, (long) http_download_ts.tv_usec); diag_stats.get_ack = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->seq) == diag_stats.get_ack && diag_stats.ack_seq == 0) { diag_stats.ack_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->ack_seq) == diag_stats.ack_seq && diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) http_download_ts.tv_usec); char *val = strstr(nexthdr, "Content-Length"); if (val) { char *pch, *pchr; diag_stats.test_bytes_received = strlen(nexthdr); val += strlen("Content-Length: "); pch = strtok_r(val, " \r\n\t", &pchr); diag_stats.test_bytes_received += DM_STRTOL(pch); diag_stats.first_data = 1; return; } } if ((strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && ntohl(tcp->ack_seq) == diag_stats.ack_seq) { snprintf(diag_stats.eomtime, sizeof(diag_stats.eomtime), "%s.%06ldZ", s_now, (long) http_download_ts.tv_usec); return; } } static void ftp_download_per_packet(libtrace_packet_t *packet) { libtrace_tcp_t *tcp = NULL; char *nexthdr, tcp_flag[16] = {0}, s_now[20] = {0}; struct tm ftp_download_lt; if (get_tcp_flag_from_packet(packet, &tcp, tcp_flag, &nexthdr)) return; struct timeval ftp_download_ts = trace_get_timeval(packet); gmtime_r(&(ftp_download_ts.tv_sec), &ftp_download_lt); strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &ftp_download_lt); if (strcmp(tcp_flag, "PSH ACK ") == 0 && DM_STRLEN(nexthdr) > strlen(FTP_SIZE_RESPONSE) && strncmp(nexthdr, FTP_SIZE_RESPONSE, strlen(FTP_SIZE_RESPONSE)) == 0) { char *val = strstr(nexthdr,"213"); char *pch, *pchr; val += strlen("213 "); pch =strtok_r(val, " \r\n\t", &pchr); diag_stats.test_bytes_received = DM_STRTOL(pch); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && DM_STRLEN(nexthdr) > strlen(FTP_PASV_RESPONSE) && strncmp(nexthdr, FTP_PASV_RESPONSE, strlen(FTP_PASV_RESPONSE)) == 0) { diag_stats.ftp_syn = 1; return; } if (diag_stats.random_seq == 0 && strcmp(tcp_flag, "SYN ") == 0 && diag_stats.ftp_syn == 1) { snprintf(diag_stats.tcpopenrequesttime, sizeof(diag_stats.tcpopenrequesttime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "SYN ACK ") == 0 && diag_stats.random_seq != 0 && (ntohl(tcp->ack_seq) - 1 ) == diag_stats.random_seq) { snprintf(diag_stats.tcpopenresponsetime, sizeof(diag_stats.tcpopenresponsetime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && DM_STRLEN(nexthdr) > strlen(FTP_RETR_REQUEST) && strncmp(nexthdr, FTP_RETR_REQUEST, strlen(FTP_RETR_REQUEST)) == 0) { snprintf(diag_stats.romtime, sizeof(diag_stats.romtime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->seq) == diag_stats.random_seq && diag_stats.ack_seq == 0) { diag_stats.ack_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->ack_seq) == diag_stats.ack_seq && diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); diag_stats.first_data = 1; return; } if ( (strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && ntohl(tcp->ack_seq) == diag_stats.ack_seq) { if (diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); diag_stats.first_data = 1; } snprintf(diag_stats.eomtime, sizeof(diag_stats.eomtime), "%s.%06ldZ", s_now, (long) ftp_download_ts.tv_usec); read_next = 0; return; } } static void http_upload_per_packet(libtrace_packet_t *packet) { libtrace_tcp_t *tcp = NULL; char *nexthdr, tcp_flag[16] = {0}, s_now[20] = {0}; struct tm http_upload_lt; if (get_tcp_flag_from_packet(packet, &tcp, tcp_flag, &nexthdr)) return; struct timeval http_upload_ts = trace_get_timeval(packet); gmtime_r(&(http_upload_ts.tv_sec), &http_upload_lt); strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &http_upload_lt); if (strcmp(tcp_flag, "SYN ") == 0 && diag_stats.random_seq == 0) { snprintf(diag_stats.tcpopenrequesttime, sizeof(diag_stats.tcpopenrequesttime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "SYN ACK ") == 0 && diag_stats.random_seq != 0 && (ntohl(tcp->ack_seq) - 1 ) == diag_stats.random_seq) { snprintf(diag_stats.tcpopenresponsetime, sizeof(diag_stats.tcpopenresponsetime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->seq); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && strncmp(nexthdr, "PUT", 3) == 0) { snprintf(diag_stats.romtime, sizeof(diag_stats.romtime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); if (strstr(nexthdr, "Expect: 100-continue")) { diag_stats.tmp = 1; diag_stats.ack_seq = ntohl(tcp->ack_seq); } else diag_stats.ack_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && diag_stats.tmp == 1 && strstr(nexthdr, "100 Continue")) { diag_stats.tmp = 2; diag_stats.ack_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && diag_stats.tmp == 2 && ntohl(tcp->seq) == diag_stats.ack_seq) { diag_stats.tmp = 0; diag_stats.ack_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->ack_seq) == diag_stats.ack_seq && diag_stats.tmp == 0 && diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); diag_stats.first_data = 1; return; } if ( (strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && ntohl(tcp->ack_seq) == diag_stats.ack_seq && diag_stats.tmp == 0) { if (diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); diag_stats.first_data = 1; } return; } if ( (strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && ntohl(tcp->seq) == diag_stats.ack_seq && diag_stats.tmp == 0) { snprintf(diag_stats.eomtime, sizeof(diag_stats.eomtime), "%s.%06ldZ", s_now, (long) http_upload_ts.tv_usec); read_next = 0; return; } } static void ftp_upload_per_packet(libtrace_packet_t *packet) { libtrace_tcp_t *tcp = NULL; char *nexthdr, tcp_flag[16] = {0}, s_now[20] = {0}; struct tm ftp_upload_lt; if (get_tcp_flag_from_packet(packet, &tcp, tcp_flag, &nexthdr)) return; if (strcmp(tcp_flag, "PSH ACK ") == 0 && DM_STRLEN(nexthdr) > strlen(FTP_PASV_RESPONSE) && strncmp(nexthdr, FTP_PASV_RESPONSE, strlen(FTP_PASV_RESPONSE)) == 0) { diag_stats.ftp_syn = 1; return; } struct timeval ftp_upload_ts = trace_get_timeval(packet); gmtime_r(&(ftp_upload_ts.tv_sec), &ftp_upload_lt); strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%S", &ftp_upload_lt); if (strcmp(tcp_flag, "SYN ") == 0 && diag_stats.ftp_syn == 1) { diag_stats.random_seq = ntohl(tcp->seq); snprintf(diag_stats.tcpopenrequesttime, sizeof(diag_stats.tcpopenrequesttime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); return; } if (strcmp(tcp_flag, "SYN ACK ") == 0 && diag_stats.random_seq != 0 && (ntohl(tcp->ack_seq) - 1 ) == diag_stats.random_seq) { snprintf(diag_stats.tcpopenresponsetime, sizeof(diag_stats.tcpopenresponsetime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); diag_stats.random_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "PSH ACK ") == 0 && DM_STRLEN(nexthdr) > strlen(FTP_STOR_REQUEST) && strncmp(nexthdr, FTP_STOR_REQUEST, strlen(FTP_STOR_REQUEST)) == 0) { snprintf(diag_stats.romtime, sizeof(diag_stats.romtime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->seq) == diag_stats.random_seq && diag_stats.ack_seq == 0) { diag_stats.ack_seq = ntohl(tcp->ack_seq); return; } if (strcmp(tcp_flag, "ACK ") == 0 && ntohl(tcp->ack_seq) == diag_stats.ack_seq && diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); diag_stats.first_data = 1; return; } if ( (strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && ntohl(tcp->ack_seq) == diag_stats.ack_seq) { if (diag_stats.first_data == 0) { snprintf(diag_stats.bomtime, sizeof(diag_stats.bomtime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); diag_stats.first_data = 1; } return; } if ( (strcmp(tcp_flag, "PSH ACK ") == 0 || strcmp(tcp_flag, "FIN PSH ACK ") == 0) && DM_STRLEN(nexthdr) > strlen(FTP_TRANSFERT_COMPLETE) && strncmp(nexthdr, FTP_TRANSFERT_COMPLETE, strlen(FTP_TRANSFERT_COMPLETE)) == 0) { snprintf(diag_stats.eomtime, sizeof(diag_stats.eomtime), "%s.%06ldZ", s_now, (long) ftp_upload_ts.tv_usec); read_next = 0; return; } } int extract_stats(char *dump_file, int proto, int diagnostic_type) { libtrace_t *trace = NULL; libtrace_packet_t *packet = trace_create_packet(); read_next = 1; if (packet == NULL) { libtrace_cleanup(trace, packet); return -1; } trace = trace_create(dump_file); if (!trace) return -1; if (trace_is_err(trace)) { libtrace_cleanup(trace, packet); return -1; } if (trace_start(trace) == -1) { libtrace_cleanup(trace, packet); return 1; } if (proto == DIAGNOSTIC_HTTP && diagnostic_type == DOWNLOAD_DIAGNOSTIC) { while (trace_read_packet(trace,packet) > 0 && read_next == 1) { http_download_per_packet(packet); continue; } set_stats_value("download"); } else if (proto == DIAGNOSTIC_FTP && diagnostic_type == DOWNLOAD_DIAGNOSTIC) { while (trace_read_packet(trace,packet) > 0 && read_next == 1) { ftp_download_per_packet(packet); continue; } set_stats_value("download"); } else if (proto == DIAGNOSTIC_HTTP && diagnostic_type == UPLOAD_DIAGNOSTIC) { while (trace_read_packet(trace,packet) > 0 && read_next == 1) { http_upload_per_packet(packet); continue; } set_stats_value("upload"); } else { while (trace_read_packet(trace,packet) > 0 && read_next == 1) { ftp_upload_per_packet(packet); continue; } set_stats_value("upload"); } libtrace_cleanup(trace, packet); return 0; }