标签:
Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。
wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。
本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1)
初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3)
如无特殊情况,分离进程,建立守护进程 (代码片段1.1)
添加多个http请求回调函数(包括404错误回调函数)
摧毁删除现有的iptables路由表规则
建立新的iptables路由表规则
启动多个功能线程
循环等待客户端连接
int main(int argc, char **argv) { s_config *config = config_get_config(); //就是返回全局变量config结构体的地址 config_init(); //初始化全局变量config结构体为默认值 parse_commandline(argc, argv); //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid) /* Initialize the config */ config_read(config->configfile); //根据配置文件设置全局变量config结构体 config_validate(); //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。 /* Initializes the linked list of connected clients */ client_list_init(); //将已连接客户端链表置空。 /* Init the signals to catch chld/quit/etc */ init_signals(); //初始化一些信号 if (restart_orig_pid) { //用于restart,如果有已运行的wifidog,先会kill它 /* * We were restarted and our parent is waiting for us to talk to it over the socket */ get_clients_from_parent(); //从已运行的wifidog中获取客户端列表,详见 代码片段1.2 /* * At this point the parent will start destroying itself and the firewall. Let it finish it‘s job before we continue */ while (kill(restart_orig_pid, 0) != -1) { //kill已运行的wifidog debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid); sleep(1); } debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading."); } if (config->daemon) { //创建为守护进程,config->daemon默认值为-1 debug(LOG_INFO, "Forking into background"); switch(safe_fork()) { case 0: /* child */ setsid(); //创建新会话,脱离此终端,实现守护进程 append_x_restartargv(); main_loop(); //进入主循环(核心代码在此)。 break; default: /* parent */ exit(0); break; } } else { append_x_restartargv(); main_loop(); } return(0); /* never reached */ }
代码片段1.2(获取已启动的wifidog的客户端列表):
此段代表描述了新启动的wifidog如何从已启动的wifidog程序中获取已连接的客户端列表。发送端见 代码片段1.3
void get_clients_from_parent(void) { int sock; struct sockaddr_un sa_un; s_config * config = NULL; char linebuffer[MAX_BUF]; int len = 0; char *running1 = NULL; char *running2 = NULL; char *token1 = NULL; char *token2 = NULL; char onechar; char *command = NULL; char *key = NULL; char *value = NULL; t_client * client = NULL; t_client * lastclient = NULL; config = config_get_config(); debug(LOG_INFO, "Connecting to parent to download clients"); /* 连接socket */ sock = socket(AF_UNIX, SOCK_STREAM, 0); memset(&sa_un, 0, sizeof(sa_un)); sa_un.sun_family = AF_UNIX; strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1)); //config->internal_sock的值为"/tmp/wifidog.sock" /* 连接已启动的wifidog */ if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) { debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno)); return; } debug(LOG_INFO, "Connected to parent. Downloading clients"); LOCK_CLIENT_LIST(); command = NULL; memset(linebuffer, 0, sizeof(linebuffer)); len = 0; client = NULL; /* 接收数据,逐个字符接收 */ /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n */ while (read(sock, &onechar, 1) == 1) { if (onechar == ‘\n‘) { /* 如果接收到末尾(‘\n‘),则转为‘\0‘ */ onechar = ‘\0‘; } linebuffer[len++] = onechar; if (!onechar) { /* 以下将数据转化为t_client结构体添加到客户端列表 */ debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer); running1 = linebuffer; while ((token1 = strsep(&running1, "|")) != NULL) { if (!command) { /* The first token is the command */ command = token1; } else { /* Token1 has something like "foo=bar" */ running2 = token1; key = value = NULL; while ((token2 = strsep(&running2, "=")) != NULL) { if (!key) { key = token2; } else if (!value) { value = token2; } } } if (strcmp(command, "CLIENT") == 0) { /* This line has info about a client in the client list */ if (!client) { /* Create a new client struct */ client = safe_malloc(sizeof(t_client)); memset(client, 0, sizeof(t_client)); } } if (key && value) { if (strcmp(command, "CLIENT") == 0) { /* Assign the key into the appropriate slot in the connection structure */ if (strcmp(key, "ip") == 0) { client->ip = safe_strdup(value); } else if (strcmp(key, "mac") == 0) { client->mac = safe_strdup(value); } else if (strcmp(key, "token") == 0) { client->token = safe_strdup(value); } else if (strcmp(key, "fw_connection_state") == 0) { client->fw_connection_state = atoi(value); } else if (strcmp(key, "fd") == 0) { client->fd = atoi(value); } else if (strcmp(key, "counters_incoming") == 0) { client->counters.incoming_history = atoll(value); client->counters.incoming = client->counters.incoming_history; } else if (strcmp(key, "counters_outgoing") == 0) { client->counters.outgoing_history = atoll(value); client->counters.outgoing = client->counters.outgoing_history; } else if (strcmp(key, "counters_last_updated") == 0) { client->counters.last_updated = atol(value); } else { debug(LOG_NOTICE, "I don‘t know how to inherit key [%s] value [%s] from parent", key, value); } } } } /* End of parsing this command */ if (client) { /* Add this client to the client list */ if (!firstclient) { firstclient = client; lastclient = firstclient; } else { lastclient->next = client; lastclient = client; } } /* Clean up */ command = NULL; memset(linebuffer, 0, sizeof(linebuffer)); len = 0; client = NULL; } } UNLOCK_CLIENT_LIST(); debug(LOG_INFO, "Client list downloaded successfully from parent"); close(sock); }
代码片段1.3(已启动的wifidog发送客户端列表到新启动的wifidog):
//thread_wdctl_handler(void *arg)函数是wifidog启动后自动创建的控制线程,主要用于与wdctrl进行socket通信,根据wdctrl命令执行不同的操作。这里我们着重讲解的是wdctrl发送restart后wifidog的执行逻辑。 static void * thread_wdctl_handler(void *arg) { int fd, done, i; char request[MAX_BUF]; ssize_t read_bytes, len; debug(LOG_DEBUG, "Entering thread_wdctl_handler...."); fd = (int)arg; debug(LOG_DEBUG, "Read bytes and stuff from %d", fd); /* 初始化变量 */ read_bytes = 0; done = 0; memset(request, 0, sizeof(request)); /* 读取命令 */ while (!done && read_bytes < (sizeof(request) - 1)) { len = read(fd, request + read_bytes, sizeof(request) - read_bytes); //读取wdctrl发送的命令 /* 判断命令正确性 */ for (i = read_bytes; i < (read_bytes + len); i++) { if (request[i] == ‘\r‘ || request[i] == ‘\n‘) { request[i] = ‘\0‘; done = 1; } } /* Increment position */ read_bytes += len; } //判断命令 if (strncmp(request, "status", 6) == 0) { wdctl_status(fd); } else if (strncmp(request, "stop", 4) == 0) { wdctl_stop(fd); } else if (strncmp(request, "reset", 5) == 0) { wdctl_reset(fd, (request + 6)); } else if (strncmp(request, "restart", 7) == 0) { wdctl_restart(fd); //执行wdctl_restart(int afd)函数 } if (!done) { debug(LOG_ERR, "Invalid wdctl request."); //关闭套接字 shutdown(fd, 2); close(fd); pthread_exit(NULL); } debug(LOG_DEBUG, "Request received: [%s]", request); //关闭套接字 shutdown(fd, 2); close(fd); debug(LOG_DEBUG, "Exiting thread_wdctl_handler...."); return NULL; } //wdctl_restart(int afd)函数详解 static void wdctl_restart(int afd) { int sock, fd; char *sock_name; struct sockaddr_un sa_un; s_config * conf = NULL; t_client * client = NULL; char * tempstring = NULL; pid_t pid; ssize_t written; socklen_t len; conf = config_get_config(); debug(LOG_NOTICE, "Will restart myself"); /* * 准备内部连接socket */ memset(&sa_un, 0, sizeof(sa_un)); sock_name = conf->internal_sock; //conf->internal_sock值为"/tmp/wifidog.sock" debug(LOG_DEBUG, "Socket name: %s", sock_name); if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) { debug(LOG_ERR, "INTERNAL socket name too long"); return; } debug(LOG_DEBUG, "Creating socket"); sock = socket(PF_UNIX, SOCK_STREAM, 0); //建立内部socket套接字 debug(LOG_DEBUG, "Got internal socket %d", sock); /* 如果sock_name文件存在,则删除*/ unlink(sock_name); debug(LOG_DEBUG, "Filling sockaddr_un"); strcpy(sa_un.sun_path, sock_name); sa_un.sun_family = AF_UNIX; debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name)); if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) { debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno)); return; } if (listen(sock, 5)) { debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno)); return; } /* * socket建立完成,创建子进程 */ debug(LOG_DEBUG, "Forking in preparation for exec()..."); pid = safe_fork(); if (pid > 0) { /* 父进程 */ /* 等待子进程连接此socket :*/ debug(LOG_DEBUG, "Waiting for child to connect on internal socket"); len = sizeof(sa_un); if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){ //接受连接 debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno)); close(sock); return; } close(sock); debug(LOG_DEBUG, "Received connection from child. Sending them all existing clients"); /*子进程已经完成连接,发送客户端列表 */ LOCK_CLIENT_LIST(); client = client_get_first_client(); //获取第一个客户端 while (client) { /* Send this client */ safe_asprintf(&tempstring, "CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n", client->ip, client->mac, client->token, client->fw_connection_state, client->fd, client->counters.incoming, client->counters.outgoing, client->counters.last_updated); debug(LOG_DEBUG, "Sending to child client data: %s", tempstring); len = 0; while (len != strlen(tempstring)) { written = write(fd, (tempstring + len), strlen(tempstring) - len); //发送给子进程 if (written == -1) { debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno)); free(tempstring); break; } else { len += written; } } free(tempstring); client = client->next; } UNLOCK_CLIENT_LIST(); close(fd); debug(LOG_INFO, "Sent all existing clients to child. Committing suicide!"); shutdown(afd, 2); close(afd); wdctl_stop(afd); } else { /* 子进程,先关闭资源 */ close(wdctl_socket_server); close(icmp_fd); close(sock); shutdown(afd, 2); close(afd); debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]); setsid(); execvp(restartargv[0], restartargv); //执行外部命令,这里重新启动wifidog debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno)); debug(LOG_ERR, "Exiting without cleanup"); exit(1); } }
本文章由http://www.wifidog.pro/2015/04/02/wifidog%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E5%88%9D%E5%A7%8B%E5%8C%96.html 整理编辑,转载请注明出处
标签:
原文地址:http://www.cnblogs.com/wifidog/p/4386445.html