Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。
wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。
本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1)
-
初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
-
初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3)
-
如无特殊情况,分离进程,建立守护进程 (代码片段1.1)
-
添加多个http请求回调函数(包括404错误回调函数) (见之后章节)
-
摧毁删除现有的iptables路由表规则 (见之后章节)
-
建立新的iptables路由表规则 (见之后章节)
-
启动多个功能线程 (见之后章节)
-
循环等待客户端连接 (见之后章节)
代码片段1.1
-
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);
-
}
-
}
小结
客户端列表只有在restart命令中才会执行,实际上流程就是
-
父wifidog准备socket
-
父wifidog启动子wifidog
-
子wifidog连接父wifidog
-
客户端列表传递
-
子wifidog终止父wifidog
阅读(10290) | 评论(0) | 转发(0) |