引言
當(dāng)wifidog啟動(dòng)時(shí)挨务,會(huì)啟動(dòng)一個(gè)線程(thread_client_timeout_check)維護(hù)客戶端列表疗垛,具體就是wifidog必須定時(shí)檢測(cè)客戶端列表中的每個(gè)客戶端是否在線梗肝,而wifidog是通過兩種方式進(jìn)行檢測(cè)客戶端在線情況之斯,一種是定時(shí)通過iptables獲取客戶端出入總流量更新客戶端時(shí)間潘悼,通過最近更新時(shí)間進(jìn)行判斷(有新的出入流量則更新客戶端時(shí)間齐唆,之后使用最新客戶端時(shí)間與當(dāng)前時(shí)間判斷)嗤栓,一種是查詢認(rèn)證服務(wù)器,通過認(rèn)證服務(wù)器的返回信息進(jìn)行判斷(將客戶端IP和狀態(tài)請(qǐng)求發(fā)送給認(rèn)證服務(wù)器,認(rèn)證服務(wù)器會(huì)返回客戶端是否在線,這種情況是用于客戶端是在認(rèn)證服務(wù)器上正常登出)。
thread_client_timeout_check
此線程執(zhí)行函數(shù)用于維護(hù)客戶端列表何陆,此線程是一個(gè)while (1)循環(huán)咒钟,每隔一個(gè)配置文件中的checkinterval時(shí)間間隔執(zhí)行一次fw_sync_with_authserver函數(shù)跪者,核心代碼處于fw_sync_with_authserver函數(shù)中,我們先具體代碼,
void
thread_client_timeout_check(const void *arg)
{
pthread_cond_t? ? ? ? cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t? ? ? ? cond_mutex = PTHREAD_MUTEX_INITIALIZER;
struct? ? timespec? ? timeout;
while (1) {
/* 設(shè)置超時(shí)時(shí)間 */
timeout.tv_sec = time(NULL) + config_get_config()->checkinterval;
timeout.tv_nsec = 0;
/* 使用pthread_cond_timedwait必須先上鎖 */
pthread_mutex_lock(&cond_mutex);
/* 等待超時(shí) */
pthread_cond_timedwait(&cond, &cond_mutex, &timeout);
/* 解鎖 */
pthread_mutex_unlock(&cond_mutex);
debug(LOG_DEBUG, "Running fw_counter()");
/* 執(zhí)行核心代碼 */
fw_sync_with_authserver();
}
}
fw_sync_with_authserver
此函數(shù)是此線程的核心函數(shù),維護(hù)客戶端列表就在此中钮呀,其首先會(huì)遍歷客戶端列表,通過iptables獲取每個(gè)客戶端列表的出入流量昨凡,之后根據(jù)出口流量(入口流量不做判斷爽醋,詳見 代碼片段1.3)更新客戶端最近更新時(shí)間(last_updated),之后使用每個(gè)客戶端最近更新時(shí)間與當(dāng)前時(shí)間比較便脊,如果超過超時(shí)間隔則判斷為下線蚂四,而如果未超時(shí),則還會(huì)從認(rèn)證服務(wù)器中獲取此客戶端狀態(tài)哪痰,確定其是否在線遂赠。具體流程如下
更新客戶端出入口流量,根據(jù)出口流量更新每個(gè)客戶端的最近更新時(shí)間
客戶端超時(shí)則從客戶端列表中移除并通過iptables禁止其訪問網(wǎng)絡(luò)妒御,并告知認(rèn)證服務(wù)器此客戶端下線
客戶端未超時(shí)則從認(rèn)證服務(wù)器獲取此客戶端信息解愤,判斷其是否通過認(rèn)證服務(wù)器下線
代碼片段1.2:
void
fw_sync_with_authserver(void)
{
t_authresponse? authresponse;
char? ? ? ? ? ? *token, *ip, *mac;
t_client? ? ? ? *p1, *p2;
unsigned long long? ? ? ? incoming, outgoing;
s_config *config = config_get_config();
/* 根據(jù)iptables流量更新最近更新時(shí)間镇饺,具體代碼見 代碼片段1.3 */
if (-1 == iptables_fw_counters_update()) {
debug(LOG_ERR, "Could not get counters from firewall!");
return;
}
LOCK_CLIENT_LIST();
/* 遍歷客戶端列表 */
for (p1 = p2 = client_get_first_client(); NULL != p1; p1 = p2) {
p2 = p1->next;
ip = safe_strdup(p1->ip);
token = safe_strdup(p1->token);
mac = safe_strdup(p1->mac);
outgoing = p1->counters.outgoing;
incoming = p1->counters.incoming;
UNLOCK_CLIENT_LIST();
/* ping一下此客戶端乎莉,不清楚作用 */
icmp_ping(ip);
/* 將客戶端的出入流量上傳至認(rèn)證服務(wù)器,此時(shí)如果此客戶端在認(rèn)證服務(wù)器上下線會(huì)返回告知wifidog */
if (config->auth_servers != NULL) {
auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, ip, mac, token, incoming, outgoing);
}
LOCK_CLIENT_LIST();
/* 從客戶端列表獲取IP,MAC對(duì)應(yīng)客戶端 */
if (!(p1 = client_list_find(ip, mac))) {
debug(LOG_ERR, "Node %s was freed while being re-validated!", ip);
} else {
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);
/* 判斷是否超時(shí)奸笤,(最近更新時(shí)間 + 超時(shí)時(shí)間 <= 當(dāng)前時(shí)間) 表明以超過超時(shí)時(shí)間惋啃,下線 */
if (p1->counters.last_updated +
(config->checkinterval * config->clienttimeout)
<= current_time) {
debug(LOG_INFO, "%s - Inactive for more than %ld seconds, removing client and denying in firewall",
p1->ip, config->checkinterval * config->clienttimeout);
/* 修改iptables禁止此客戶端訪問外網(wǎng) */
fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
/* 從客戶端列表中刪除此客戶端 */
client_list_delete(p1);
/* 通知認(rèn)證服務(wù)器此客戶端下線 */
if (config->auth_servers != NULL) {
UNLOCK_CLIENT_LIST();
auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token, 0, 0);
LOCK_CLIENT_LIST();
}
} else {
/* 未超時(shí)處理 */
if (config->auth_servers != NULL) {
/* 判斷認(rèn)證服務(wù)器返回信息 */
switch (authresponse.authcode) {
/* 認(rèn)證服務(wù)器禁止其訪問網(wǎng)絡(luò)(下線或遭拒絕) */
case AUTH_DENIED:
debug(LOG_NOTICE, "%s - Denied. Removing client and firewall rules", p1->ip);
fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
client_list_delete(p1);
break;
case AUTH_VALIDATION_FAILED:
debug(LOG_NOTICE, "%s - Validation timeout, now denied. Removing client and firewall rules", p1->ip);
fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
client_list_delete(p1);
break;
/* 認(rèn)證服務(wù)器允許其訪問網(wǎng)絡(luò)(在線) */
case AUTH_ALLOWED:
if (p1->fw_connection_state != FW_MARK_KNOWN) {
debug(LOG_INFO, "%s - Access has changed to allowed, refreshing firewall and clearing counters", p1->ip);
if (p1->fw_connection_state != FW_MARK_PROBATION) {
p1->counters.incoming = p1->counters.outgoing = 0;
}
else {
debug(LOG_INFO, "%s - Skipped clearing counters after all, the user was previously in validation", p1->ip);
}
p1->fw_connection_state = FW_MARK_KNOWN;
fw_allow(p1->ip, p1->mac, p1->fw_connection_state);
}
break;
case AUTH_VALIDATION:
debug(LOG_INFO, "%s - User in validation period", p1->ip);
break;
case AUTH_ERROR:
debug(LOG_WARNING, "Error communicating with auth server - leaving %s as-is for now", p1->ip);
break;
default:
debug(LOG_ERR, "I do not know about authentication code %d", authresponse.authcode);
break;
}
}
}
}
free(token);
free(ip);
free(mac);
}
UNLOCK_CLIENT_LIST();
}
代碼片段1.3:
int
iptables_fw_counters_update(void)
{
FILE *output;
char *script,
ip[16],
rc;
unsigned long long int counter;
t_client *p1;
struct in_addr tempaddr;
/* 通過iptables獲取其出口流量 */
safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
iptables_insert_gateway_id(&script);
output = popen(script, "r");
free(script);
if (!output) {
debug(LOG_ERR, "popen(): %s", strerror(errno));
return -1;
}
/* iptables返回信息處理 */
while (('\n' != fgetc(output)) && !feof(output))
;
while (('\n' != fgetc(output)) && !feof(output))
;
while (output && !(feof(output))) {
rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s %*s", &counter, ip);
//rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
if (2 == rc && EOF != rc) {
if (!inet_aton(ip, &tempaddr)) {
debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
continue;
}
debug(LOG_DEBUG, "Read outgoing traffic for %s: Bytes=%llu", ip, counter);
LOCK_CLIENT_LIST();
/* 通過ip獲取客戶端信息結(jié)構(gòu) */
if ((p1 = client_list_find_by_ip(ip))) {
/* (上一次出口總流量(outgoing) + wifidog啟動(dòng)時(shí)的出口總流量(outgoing_history) < iptables返回的出口總流量) 表示此客戶端有新的出口流量 */
if ((p1->counters.outgoing - p1->counters.outgoing_history) < counter) {
/* 更新上一次出口總流量(outgoing)為wifidog啟動(dòng)時(shí)的出口總流量(outgoing_history) + iptables返回總流量(counter) */
p1->counters.outgoing = p1->counters.outgoing_history + counter;
/* 更新最近更新時(shí)間為當(dāng)前時(shí)間 */
p1->counters.last_updated = time(NULL);
debug(LOG_DEBUG, "%s - Updated counter.outgoing to %llu bytes.? Updated last_updated to %d", ip, counter, p1->counters.last_updated);
}
} else {
debug(LOG_ERR, "Could not find %s in client list", ip);
}
UNLOCK_CLIENT_LIST();
}
}
pclose(output);
/* 通過iptables獲取其入口流量,入口流量不做更新最近更新時(shí)間參考监右,只用于更新后上傳至認(rèn)證服務(wù)器边灭,其原理同上,后面的代碼不做詳細(xì)分析 */
safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
iptables_insert_gateway_id(&script);
output = popen(script, "r");
free(script);
if (!output) {
debug(LOG_ERR, "popen(): %s", strerror(errno));
return -1;
}
while (('\n' != fgetc(output)) && !feof(output))
;
while (('\n' != fgetc(output)) && !feof(output))
;
while (output && !(feof(output))) {
rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
if (2 == rc && EOF != rc) {
if (!inet_aton(ip, &tempaddr)) {
debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
continue;
}
debug(LOG_DEBUG, "Read incoming traffic for %s: Bytes=%llu", ip, counter);
LOCK_CLIENT_LIST();
if ((p1 = client_list_find_by_ip(ip))) {
if ((p1->counters.incoming - p1->counters.incoming_history) < counter) {
p1->counters.incoming = p1->counters.incoming_history + counter;
debug(LOG_DEBUG, "%s - Updated counter.incoming to %llu bytes", ip, counter);
}
} else {
debug(LOG_ERR, "Could not find %s in client list", ip);
}
UNLOCK_CLIENT_LIST();
}
}
pclose(output);
return 1;
}
本文由http://www.wifidog.pro/2015/02/02/wifidog%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%A3%80%E6%B5%8B.html整理編輯健盒,轉(zhuǎn)載請(qǐng)注明出處