/** See if they are still active, * refresh their traffic counters, * remove and deny them if timed out */ static void fw_refresh_client_list(void) { t_client *cp1, *cp2; s_config *config = config_get_config(); const int preauth_idle_timeout_secs = 60 * config->preauth_idle_timeout; const int auth_idle_timeout_secs = 60 * config->auth_idle_timeout; const time_t now = time(NULL); /* Update all the counters */ if (-1 == iptables_fw_counters_update()) { debug(LOG_ERR, "Could not get counters from firewall!"); return; } LOCK_CLIENT_LIST(); for (cp1 = cp2 = client_get_first_client(); NULL != cp1; cp1 = cp2) { cp2 = cp1->next; if (!(cp1 = client_list_find_by_id(cp1->id))) { debug(LOG_ERR, "Client was freed while being re-validated!"); continue; } int conn_state = cp1->fw_connection_state; int last_updated = cp1->counters.last_updated; if (cp1->session_end > 0 && cp1->session_end <= now) { /* Session ended (only > 0 for FW_MARK_AUTHENTICATED by binauth) */ debug(LOG_NOTICE, "Force out user: %s %s, connected: %ds, in: %llukB, out: %llukB", cp1->ip, cp1->mac, now - cp1->session_end, cp1->counters.incoming / 1000, cp1->counters.outgoing / 1000); auth_change_state(cp1, FW_MARK_PREAUTHENTICATED, "timeout_deauth"); } else if (preauth_idle_timeout_secs > 0 && conn_state == FW_MARK_PREAUTHENTICATED && (last_updated + preauth_idle_timeout_secs) <= now) { /* Timeout inactive preauthenticated user */ debug(LOG_NOTICE, "Timeout preauthenticated idle user: %s %s, inactive: %ds, in: %llukB, out: %llukB", cp1->ip, cp1->mac, now - last_updated, cp1->counters.incoming / 1000, cp1->counters.outgoing / 1000); client_list_delete(cp1); } else if (auth_idle_timeout_secs > 0 && conn_state == FW_MARK_AUTHENTICATED && (last_updated + auth_idle_timeout_secs) <= now) { /* Timeout inactive user */ debug(LOG_NOTICE, "Timeout authenticated idle user: %s %s, inactive: %ds, in: %llukB, out: %llukB", cp1->ip, cp1->mac, now - last_updated, cp1->counters.incoming / 1000, cp1->counters.outgoing / 1000); auth_change_state(cp1, FW_MARK_PREAUTHENTICATED, "idle_deauth"); } } UNLOCK_CLIENT_LIST(); }
void ndsctl_json(int fd) { t_client *client; int indx; unsigned long int now, durationsecs = 0; unsigned long long int download_bytes, upload_bytes; now = time(NULL); /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); cprintf(fd, "{\n\"client_length\": %d,\n", get_client_list_length()); client = client_get_first_client(); indx = 0; cprintf(fd, "\"clients\":{\n"); while (client != NULL) { cprintf(fd, "\"%s\":{\n", client->mac); cprintf(fd, "\"client_id\":%d,\n", indx); cprintf(fd, "\"ip\":\"%s\",\n\"mac\":\"%s\",\n", client->ip, client->mac); cprintf(fd, "\"added\":%lld,\n", (long long) client->added_time); cprintf(fd, "\"active\":%lld,\n", (long long) client->counters.last_updated); cprintf(fd, "\"duration\":%lu,\n", now - client->added_time); cprintf(fd, "\"token\":\"%s\",\n", client->token ? client->token : "none"); cprintf(fd, "\"state\":\"%s\",\n", fw_connection_state_as_string(client->fw_connection_state)); durationsecs = now - client->added_time; download_bytes = client->counters.incoming; upload_bytes = client->counters.outgoing; cprintf(fd, "\"downloaded\":\"%llu\",\n\"avg_down_speed\":\"%.6g\",\n\"uploaded\":\"%llu\",\n\"avg_up_speed\":\"%.6g\"\n", download_bytes/1000, ((double)download_bytes)/125/durationsecs, upload_bytes/1000, ((double)upload_bytes)/125/durationsecs); indx++; client = client->next; cprintf(fd, "}"); if(client) { cprintf(fd, ",\n"); } } cprintf(fd, "}}" ); UNLOCK_CLIENT_LIST(); }
/** Ping clients to see if they are still active, * refresh their traffic counters, * remove and deny them if timed out */ void fw_refresh_client_list(void) { t_client *cp1, *cp2; time_t now, added_time, last_updated; s_config *config = config_get_config(); /* Update all the counters */ if (-1 == iptables_fw_counters_update()) { debug(LOG_ERR, "Could not get counters from firewall!"); return; } LOCK_CLIENT_LIST(); for (cp1 = cp2 = client_get_first_client(); NULL != cp1; cp1 = cp2) { cp2 = cp1->next; if (!(cp1 = client_list_find(cp1->ip, cp1->mac))) { debug(LOG_ERR, "Node %s was freed while being re-validated!", cp1->ip); } else { now = time(NULL); last_updated = cp1->counters.last_updated; added_time = cp1->added_time; if (last_updated + (config->checkinterval * config->clienttimeout) <= now) { /* Timing out inactive user */ debug(LOG_NOTICE, "%s %s inactive %d secs. kB in: %llu kB out: %llu", cp1->ip, cp1->mac, config->checkinterval * config->clienttimeout, cp1->counters.incoming/1000, cp1->counters.outgoing/1000); if (cp1->fw_connection_state == FW_MARK_AUTHENTICATED) { iptables_fw_access(AUTH_MAKE_DEAUTHENTICATED, cp1); } client_list_delete(cp1); } else if (added_time + (config->checkinterval * config->clientforceout) <= now) { /* Forcing out user */ debug(LOG_NOTICE, "%s %s connected %d secs. kB in: %llu kB out: %llu", cp1->ip, cp1->mac, config->checkinterval * config->clientforceout, cp1->counters.incoming/1000, cp1->counters.outgoing/1000); if (cp1->fw_connection_state == FW_MARK_AUTHENTICATED) { iptables_fw_access(AUTH_MAKE_DEAUTHENTICATED, cp1); } client_list_delete(cp1); } } } UNLOCK_CLIENT_LIST(); }
void ndsctl_clients(int fd) { t_client *client; int indx; unsigned long int now, durationsecs = 0; unsigned long long int download_bytes, upload_bytes; now = time(NULL); /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); cprintf(fd, "%d\n", get_client_list_length()); client = client_get_first_client(); if(client) { cprintf(fd, "\n"); } indx = 0; while (client != NULL) { cprintf(fd, "client_id=%d\n", indx); cprintf(fd, "ip=%s\nmac=%s\n", client->ip, client->mac); cprintf(fd, "added=%lld\n", (long long) client->added_time); cprintf(fd, "active=%lld\n", (long long) client->counters.last_updated); cprintf(fd, "duration=%lu\n", now - client->added_time); cprintf(fd, "token=%s\n", client->token ? client->token : "none"); cprintf(fd, "state=%s\n", fw_connection_state_as_string(client->fw_connection_state)); durationsecs = now - client->added_time; download_bytes = client->counters.incoming; upload_bytes = client->counters.outgoing; cprintf(fd, "downloaded=%llu\navg_down_speed=%.6g\nuploaded=%llu\navg_up_speed=%.6g\n\n", download_bytes/1000, ((double)download_bytes)/125/durationsecs, upload_bytes/1000, ((double)upload_bytes)/125/durationsecs); indx++; client = client->next; } UNLOCK_CLIENT_LIST(); }
/**Probably a misnomer, this function actually refreshes the entire client list's traffic counter, re-authenticates every client with the central server and update's the central servers traffic counters and notifies it if a client has logged-out. * @todo Make this function smaller and use sub-fonctions */ void fw_sync_with_authserver(void) { t_authresponse authresponse; t_client *p1, *p2, *worklist, *tmp; s_config *config = config_get_config(); if (-1 == iptables_fw_counters_update()) { debug(LOG_ERR, "Could not get counters from firewall!"); return; } LOCK_CLIENT_LIST(); /* XXX Ideally, from a thread safety PoV, this function should build a list of client pointers, * iterate over the list and have an explicit "client still valid" check while list is locked. * That way clients can disappear during the cycle with no risk of trashing the heap or getting * a SIGSEGV. */ client_list_dup(&worklist); UNLOCK_CLIENT_LIST(); for (p1 = p2 = worklist; NULL != p1; p1 = p2) { p2 = p1->next; /* Ping the client, if he responds it'll keep activity on the link. * However, if the firewall blocks it, it will not help. The suggested * way to deal witht his is to keep the DHCP lease time extremely * short: Shorter than config->checkinterval * config->clienttimeout */ icmp_ping(p1->ip); /* Update the counters on the remote server only if we have an auth server */ if (config->auth_servers != NULL) { auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, p1->ip, p1->mac, p1->token, p1->counters.incoming, p1->counters.outgoing, p1->counters.incoming_delta, p1->counters.outgoing_delta); } time_t current_time = time(NULL); debug(LOG_INFO, "Checking client %s for timeout: Last updated %ld (%ld seconds ago), timeout delay %ld seconds, current time %ld, ", p1->ip, p1->counters.last_updated, current_time - p1->counters.last_updated, config->checkinterval * config->clienttimeout, current_time); if (p1->counters.last_updated + (config->checkinterval * config->clienttimeout) <= current_time) { /* Timing out user */ debug(LOG_INFO, "%s - Inactive for more than %ld seconds, removing client and denying in firewall", p1->ip, config->checkinterval * config->clienttimeout); LOCK_CLIENT_LIST(); tmp = client_list_find_by_client(p1); if (NULL != tmp) { logout_client(tmp); } else { debug(LOG_NOTICE, "Client was already removed. Not logging out."); } UNLOCK_CLIENT_LIST(); } else { /* * This handles any change in * the status this allows us * to change the status of a * user while he's connected * * Only run if we have an auth server * configured! */ LOCK_CLIENT_LIST(); tmp = client_list_find_by_client(p1); if (NULL == tmp) { UNLOCK_CLIENT_LIST(); debug(LOG_NOTICE, "Client was already removed. Skipping auth processing"); continue; /* Next client please */ } if (config->auth_servers != NULL) { switch (authresponse.authcode) { case AUTH_DENIED: debug(LOG_NOTICE, "%s - Denied. Removing client and firewall rules", tmp->ip); fw_deny(tmp); client_list_delete(tmp); break; case AUTH_VALIDATION_FAILED: debug(LOG_NOTICE, "%s - Validation timeout, now denied. Removing client and firewall rules", tmp->ip); fw_deny(tmp); client_list_delete(tmp); break; case AUTH_ALLOWED: if (tmp->fw_connection_state != FW_MARK_KNOWN) { debug(LOG_INFO, "%s - Access has changed to allowed, refreshing firewall and clearing counters", tmp->ip); //WHY did we deny, then allow!?!? benoitg 2007-06-21 //fw_deny(tmp->ip, tmp->mac, tmp->fw_connection_state); /* XXX this was possibly to avoid dupes. */ if (tmp->fw_connection_state != FW_MARK_PROBATION) { tmp->counters.incoming_delta = tmp->counters.outgoing_delta = tmp->counters.incoming = tmp->counters.outgoing = 0; } else { //We don't want to clear counters if the user was in validation, it probably already transmitted data.. debug(LOG_INFO, "%s - Skipped clearing counters after all, the user was previously in validation", tmp->ip); } fw_allow(tmp, FW_MARK_KNOWN); } break; case AUTH_VALIDATION: /* * Do nothing, user * is in validation * period */ debug(LOG_INFO, "%s - User in validation period", tmp->ip); break; case AUTH_ERROR: debug(LOG_WARNING, "Error communicating with auth server - leaving %s as-is for now", tmp->ip); break; default: debug(LOG_ERR, "I do not know about authentication code %d", authresponse.authcode); break; } } UNLOCK_CLIENT_LIST(); } } client_list_destroy(worklist); }
/* * @return A string containing json clients list. */ char * get_clients_json(void) { char buffer[STATUS_BUF_SIZ]; ssize_t len; t_client *client; int indx; unsigned long int now, durationsecs = 0; unsigned long long int download_bytes, upload_bytes; now = time(NULL); len = 0; /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "{\n\"client_length\": %d,\n", get_client_list_length()); len = strlen(buffer); client = client_get_first_client(); indx = 0; snprintf((buffer + len), (sizeof(buffer) - len), "\"clients\":{\n"); len = strlen(buffer); while (client != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "\"%s\":{\n", client->mac); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"client_id\":%d,\n", indx); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"ip\":\"%s\",\n\"mac\":\"%s\",\n", client->ip, client->mac); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"added\":%lld,\n", (long long) client->added_time); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"active\":%lld,\n", (long long) client->counters.last_updated); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"duration\":%lu,\n", now - client->added_time); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"token\":\"%s\",\n", client->token ? client->token : "none"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "\"state\":\"%s\",\n", fw_connection_state_as_string(client->fw_connection_state)); len = strlen(buffer); durationsecs = now - client->added_time; download_bytes = client->counters.incoming; upload_bytes = client->counters.outgoing; snprintf((buffer + len), (sizeof(buffer) - len), "\"downloaded\":\"%llu\",\n\"avg_down_speed\":\"%.6g\",\n\"uploaded\":\"%llu\",\n\"avg_up_speed\":\"%.6g\"\n", download_bytes/1000, ((double)download_bytes)/125/durationsecs, upload_bytes/1000, ((double)upload_bytes)/125/durationsecs); len = strlen(buffer); indx++; client = client->next; snprintf((buffer + len), (sizeof(buffer) - len), "}"); len = strlen(buffer); if(client) { snprintf((buffer + len), (sizeof(buffer) - len), ",\n"); len = strlen(buffer); } } snprintf((buffer + len), (sizeof(buffer) - len), "}}" ); len = strlen(buffer); UNLOCK_CLIENT_LIST(); return safe_strdup(buffer); }
/* * @return A string containing human-readable status text. * MUST BE free()d by caller */ char * get_status_text() { char buffer[STATUS_BUF_SIZ]; char timebuf[32]; char * str; ssize_t len; s_config *config; t_client *client; int indx; unsigned long int now, uptimesecs, durationsecs = 0; unsigned long long int download_bytes, upload_bytes; t_MAC *trust_mac; t_MAC *allow_mac; t_MAC *block_mac; config = config_get_config(); len = 0; snprintf(buffer, (sizeof(buffer) - len), "==================\nNoDogSplash Status\n====\n"); len = strlen(buffer); now = time(NULL); uptimesecs = now - started_time; snprintf((buffer + len), (sizeof(buffer) - len), "Version: " VERSION "\n"); len = strlen(buffer); str = format_time(uptimesecs); snprintf((buffer + len), (sizeof(buffer) - len), "Uptime: %s\n", str); len = strlen(buffer); free(str); snprintf((buffer + len), (sizeof(buffer) - len), "Gateway Name: %s\n", config->gw_name); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Managed interface: %s\n", config->gw_interface); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Managed IP range: %s\n", config->gw_iprange); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Server listening: %s:%d\n", config->gw_address, config->gw_port); len = strlen(buffer); if(config->authenticate_immediately) { snprintf((buffer + len), (sizeof(buffer) - len), "Authenticate immediately: yes\n"); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Splashpage: %s/%s\n", config->webroot, config->splashpage); len = strlen(buffer); } if(config->redirectURL) { snprintf((buffer + len), (sizeof(buffer) - len), "Redirect URL: %s\n", config->redirectURL); len = strlen(buffer); } if(config->passwordauth) { snprintf((buffer + len), (sizeof(buffer) - len), "Gateway password: %s\n", config->password); len = strlen(buffer); } if(config->usernameauth) { snprintf((buffer + len), (sizeof(buffer) - len), "Gateway username: %s\n", config->username); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "Traffic control: %s\n", config->traffic_control ? "yes" : "no"); len = strlen(buffer); if(config->traffic_control) { if(config->download_limit > 0) { snprintf((buffer + len), (sizeof(buffer) - len), "Download rate limit: %d kbit/s\n", config->download_limit); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Download rate limit: none\n"); len = strlen(buffer); } if(config->upload_limit > 0) { snprintf((buffer + len), (sizeof(buffer) - len), "Upload rate limit: %d kbit/s\n", config->upload_limit); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Upload rate limit: none\n"); len = strlen(buffer); } } download_bytes = iptables_fw_total_download(); snprintf((buffer + len), (sizeof(buffer) - len), "Total download: %llu kByte", download_bytes/1000); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "; avg: %.6g kbit/s\n", ((double) download_bytes) / 125 / uptimesecs); len = strlen(buffer); upload_bytes = iptables_fw_total_upload(); snprintf((buffer + len), (sizeof(buffer) - len), "Total upload: %llu kByte", upload_bytes/1000); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "; avg: %.6g kbit/s\n", ((double) upload_bytes) / 125 / uptimesecs); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "====\n"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Client authentications since start: %u\n", authenticated_since_start); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Httpd request threads created/current: %d/%d\n", created_httpd_threads, current_httpd_threads); len = strlen(buffer); if(config->decongest_httpd_threads) { snprintf((buffer + len), (sizeof(buffer) - len), "Httpd thread decongest threshold: %d threads\n", config->httpd_thread_threshold); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Httpd thread decongest delay: %d ms\n", config->httpd_thread_delay_ms); len = strlen(buffer); } /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "Current clients: %d\n", get_client_list_length()); len = strlen(buffer); client = client_get_first_client(); if(client) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); } indx = 0; while (client != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "Client %d\n", indx); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), " IP: %s MAC: %s\n", client->ip, client->mac); len = strlen(buffer); ctime_r(&(client->added_time),timebuf); snprintf((buffer + len), (sizeof(buffer) - len), " Added: %s", timebuf); len = strlen(buffer); ctime_r(&(client->counters.last_updated),timebuf); snprintf((buffer + len), (sizeof(buffer) - len), " Active: %s", timebuf); len = strlen(buffer); str = format_time(client->counters.last_updated - client->added_time); snprintf((buffer + len), (sizeof(buffer) - len), " Active duration: %s\n", str); len = strlen(buffer); free(str); durationsecs = now - client->added_time; str = format_time(durationsecs); snprintf((buffer + len), (sizeof(buffer) - len), " Added duration: %s\n", str); len = strlen(buffer); free(str); snprintf((buffer + len), (sizeof(buffer) - len), " Token: %s\n", client->token ? client->token : "none"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), " State: %s\n", fw_connection_state_as_string(client->fw_connection_state)); len = strlen(buffer); download_bytes = client->counters.incoming; upload_bytes = client->counters.outgoing; snprintf((buffer + len), (sizeof(buffer) - len), " Download: %llu kByte; avg: %.6g kbit/s\n Upload: %llu kByte; avg: %.6g kbit/s\n\n", download_bytes/1000, ((double)download_bytes)/125/durationsecs, upload_bytes/1000, ((double)upload_bytes)/125/durationsecs); len = strlen(buffer); indx++; client = client->next; } UNLOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "====\n"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Blocked MAC addresses:"); len = strlen(buffer); if(config->macmechanism == MAC_ALLOW) { snprintf((buffer + len), (sizeof(buffer) - len), " N/A\n"); len = strlen(buffer); } else if (config->blockedmaclist != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); for (block_mac = config->blockedmaclist; block_mac != NULL; block_mac = block_mac->next) { snprintf((buffer + len), (sizeof(buffer) - len), " %s\n", block_mac->mac); len = strlen(buffer); } } else { snprintf((buffer + len), (sizeof(buffer) - len), " none\n"); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "Allowed MAC addresses:"); len = strlen(buffer); if(config->macmechanism == MAC_BLOCK) { snprintf((buffer + len), (sizeof(buffer) - len), " N/A\n"); len = strlen(buffer); } else if (config->allowedmaclist != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); for (allow_mac = config->allowedmaclist; allow_mac != NULL; allow_mac = allow_mac->next) { snprintf((buffer + len), (sizeof(buffer) - len), " %s\n", allow_mac->mac); len = strlen(buffer); } } else { snprintf((buffer + len), (sizeof(buffer) - len), " none\n"); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "Trusted MAC addresses:"); len = strlen(buffer); if (config->trustedmaclist != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); for (trust_mac = config->trustedmaclist; trust_mac != NULL; trust_mac = trust_mac->next) { snprintf((buffer + len), (sizeof(buffer) - len), " %s\n", trust_mac->mac); len = strlen(buffer); } } else { snprintf((buffer + len), (sizeof(buffer) - len), " none\n"); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "========\n"); len = strlen(buffer); return safe_strdup(buffer); }
/* * @return A string containing machine-readable clients list. * MUST BE free()d by caller */ char * get_clients_text() { char buffer[STATUS_BUF_SIZ]; ssize_t len; t_client *client; int indx; unsigned long int now, durationsecs = 0; unsigned long long int download_bytes, upload_bytes; now = time(NULL); len = 0; /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "%d\n", get_client_list_length()); len = strlen(buffer); client = client_get_first_client(); if(client) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); } indx = 0; while (client != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "client_id=%d\n", indx); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "ip=%s\nmac=%s\n", client->ip, client->mac); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "added=%d\n", client->added_time); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "active=%d\n", client->counters.last_updated); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "duration=%d\n", now - client->added_time); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "token=%s\n", client->token ? client->token : "none"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "state=%s\n", fw_connection_state_as_string(client->fw_connection_state)); len = strlen(buffer); durationsecs = now - client->added_time; download_bytes = client->counters.incoming; upload_bytes = client->counters.outgoing; snprintf((buffer + len), (sizeof(buffer) - len), "downloaded=%llu\navg_down_speed=%.6g\nuploaded=%llu\navg_up_speed=%.6g\n\n", download_bytes/1000, ((double)download_bytes)/125/durationsecs, upload_bytes/1000, ((double)upload_bytes)/125/durationsecs); len = strlen(buffer); indx++; client = client->next; } UNLOCK_CLIENT_LIST(); return safe_strdup(buffer); }