找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2228|回复: 0

wifidog源码分析 - 客户端检测线程

[复制链接]
发表于 2015-2-3 16:34:27 | 显示全部楼层 |阅读模式
引言
  当wifidog启动时,会启动一个线程(thread_client_timeout_check)维护客户端列表,具体就是wifidog必须定时检测客户端列表中的每个客户端是否在线,而wifidog是通过两种方式进行检测客户端在线情况,一种是定时通过iptables获取客户端出入总流量更新客户端时间,通过最近更新时间进行判断(有新的出入流量则更新客户端时间,之后使用最新客户端时间与当前时间判断),一种是查询认证服务器,通过认证服务器的返回信息进行判断(将客户端IP和状态请求发送给认证服务器,认证服务器会返回客户端是否在线,这种情况是用于客户端是在认证服务器上正常登出)。
thread_client_timeout_check
此线程执行函数用于维护客户端列表,此线程是一个while (1)循环,每隔一个配置文件中的checkinterval时间间隔执行一次fw_sync_with_authserver函数,核心代码处于fw_sync_with_authserver函数中,我们先具体代码,
voidthread_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) {        /* 设置超时时间 */        timeout.tv_sec = time(NULL) + config_get_config()->checkinterval;        timeout.tv_nsec = 0;        /* 使用pthread_cond_timedwait必须先上锁 */        pthread_mutex_lock(&cond_mutex);        /* 等待超时 */        pthread_cond_timedwait(&cond, &cond_mutex, &timeout);        /* 解锁 */        pthread_mutex_unlock(&cond_mutex);        debug(LOG_DEBUG, "Running fw_counter()");        /* 执行核心代码 */          fw_sync_with_authserver();    }}
fw_sync_with_authserver
  此函数是此线程的核心函数,维护客户端列表就在此中,其首先会遍历客户端列表,通过iptables获取每个客户端列表的出入流量,之后根据出口流量(入口流量不做判断,详见 代码片段1.3)更新客户端最近更新时间(last_updated),之后使用每个客户端最近更新时间与当前时间比较,如果超过超时间隔则判断为下线,而如果未超时,则还会从认证服务器中获取此客户端状态,确定其是否在线。具体流程如下
更新客户端出入口流量,根据出口流量更新每个客户端的最近更新时间
客户端超时则从客户端列表中移除并通过iptables禁止其访问网络,并告知认证服务器此客户端下线
客户端未超时则从认证服务器获取此客户端信息,判断其是否通过认证服务器下线
代码片段1.2:
voidfw_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();    /* 根据iptables流量更新最近更新时间,具体代码见 代码片段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);        /* 将客户端的出入流量上传至认证服务器,此时如果此客户端在认证服务器上下线会返回告知wifidog */        if (config->auth_servers != NULL) {            auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, ip, mac, token, incoming, outgoing);        }        LOCK_CLIENT_LIST();        /* 从客户端列表获取IP,MAC对应客户端 */        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);            /* 判断是否超时,(最近更新时间 + 超时时间 <= 当前时间) 表明以超过超时时间,下线 */            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禁止此客户端访问外网 */                fw_deny(p1->ip, p1->mac, p1->fw_connection_state);                /* 从客户端列表中删除此客户端 */                client_list_delete(p1);                /* 通知认证服务器此客户端下线 */                if (config->auth_servers != NULL) {                    UNLOCK_CLIENT_LIST();                    auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token, 0, 0);                    LOCK_CLIENT_LIST();                }            } else {                /* 未超时处理 */                if (config->auth_servers != NULL) {                    /* 判断认证服务器返回信息 */                    switch (authresponse.authcode) {                        /* 认证服务器禁止其访问网络(下线或遭拒绝) */                        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;                        /* 认证服务器允许其访问网络(在线) */                        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:
intiptables_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获取客户端信息结构 */            if ((p1 = client_list_find_by_ip(ip))) {                /* (上一次出口总流量(outgoing) + wifidog启动时的出口总流量(outgoing_history) < iptables返回的出口总流量) 表示此客户端有新的出口流量 */                if ((p1->counters.outgoing - p1->counters.outgoing_history) < counter) {                    /* 更新上一次出口总流量(outgoing)为wifidog启动时的出口总流量(outgoing_history) + iptables返回总流量(counter) */                    p1->counters.outgoing = p1->counters.outgoing_history + counter;                    /* 更新最近更新时间为当前时间 */                    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获取其入口流量,入口流量不做更新最近更新时间参考,只用于更新后上传至认证服务器,其原理同上,后面的代码不做详细分析 */    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;}原文转自:www.wifidog.pro

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|小黑屋|宽带技术网 |网站地图 粤公网安备44152102000001号

GMT+8, 2025-9-19 21:49 , Processed in 0.022289 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5 Licensed

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表