wifidog源码分析 - 初始化阶段

Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。

wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。

本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1

  • 初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
  • 初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3
  • 如无特殊情况,分离进程,建立守护进程 (代码片段1.1
  • 添加多个http请求回调函数(包括404错误回调函数) (见之后章节)
  • 摧毁删除现有的iptables路由表规则 (见之后章节)
  • 建立新的iptables路由表规则 (见之后章节)
  • 启动多个功能线程 (见之后章节)
  • 循环等待客户端连接 (见之后章节)

代码片段1.1

 1 int main(int argc, char **argv) {
 2
 3     s_config *config = config_get_config();  //就是返回全局变量config结构体的地址
 4     config_init();    //初始化全局变量config结构体为默认值
 5
 6     parse_commandline(argc, argv);    //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid)
 7
 8     /* Initialize the config */
 9     config_read(config->configfile);    //根据配置文件设置全局变量config结构体
10     config_validate();    //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。
11
12     /* Initializes the linked list of connected clients */
13     client_list_init();    //将已连接客户端链表置空。
14
15     /* Init the signals to catch chld/quit/etc */
16     init_signals();    //初始化一些信号
17
18     if (restart_orig_pid) {    //用于restart,如果有已运行的wifidog,先会kill它
19         /*
20          * We were restarted and our parent is waiting for us to talk to it over the socket
21          */
22         get_clients_from_parent();    //从已运行的wifidog中获取客户端列表,详见 代码片段1.2
23
24         /*
25          * At this point the parent will start destroying itself and the firewall. Let it finish it‘s job before we continue
26          */
27
28         while (kill(restart_orig_pid, 0) != -1) {    //kill已运行的wifidog
29             debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
30             sleep(1);
31         }
32
33         debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
34     }
35
36     if (config->daemon) {    //创建为守护进程,config->daemon默认值为-1
37
38         debug(LOG_INFO, "Forking into background");
39
40         switch(safe_fork()) {
41             case 0: /* child */
42                 setsid();    //创建新会话,脱离此终端,实现守护进程
43                 append_x_restartargv();
44                 main_loop();    //进入主循环(核心代码在此)。
45                 break;
46
47             default: /* parent */
48                 exit(0);
49                 break;
50         }
51     }
52     else {
53         append_x_restartargv();
54         main_loop();
55     }
56
57     return(0); /* never reached */
58 }

代码片段1.2(获取已启动的wifidog的客户端列表):

此段代表描述了新启动的wifidog如何从已启动的wifidog程序中获取已连接的客户端列表。发送端见 代码片段1.3

  1 void get_clients_from_parent(void) {
  2     int sock;
  3     struct sockaddr_un    sa_un;
  4     s_config * config = NULL;
  5     char linebuffer[MAX_BUF];
  6     int len = 0;
  7     char *running1 = NULL;
  8     char *running2 = NULL;
  9     char *token1 = NULL;
 10     char *token2 = NULL;
 11     char onechar;
 12     char *command = NULL;
 13     char *key = NULL;
 14     char *value = NULL;
 15     t_client * client = NULL;
 16     t_client * lastclient = NULL;
 17
 18     config = config_get_config();
 19
 20     debug(LOG_INFO, "Connecting to parent to download clients");
 21
 22     /* 连接socket */
 23     sock = socket(AF_UNIX, SOCK_STREAM, 0);
 24     memset(&sa_un, 0, sizeof(sa_un));
 25     sa_un.sun_family = AF_UNIX;
 26     strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1));    //config->internal_sock的值为"/tmp/wifidog.sock"
 27
 28     /* 连接已启动的wifidog */
 29     if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
 30         debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
 31         return;
 32     }
 33
 34     debug(LOG_INFO, "Connected to parent.  Downloading clients");
 35
 36     LOCK_CLIENT_LIST();
 37
 38     command = NULL;
 39     memset(linebuffer, 0, sizeof(linebuffer));
 40     len = 0;
 41     client = NULL;
 42     /* 接收数据,逐个字符接收 */
 43     /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n */
 44     while (read(sock, &onechar, 1) == 1) {
 45         if (onechar == ‘\n‘) {
 46             /* 如果接收到末尾(‘\n‘),则转为‘\0‘ */
 47             onechar = ‘\0‘;
 48         }
 49         linebuffer[len++] = onechar;
 50
 51         if (!onechar) {
 52             /* 以下将数据转化为t_client结构体添加到客户端列表 */
 53             debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
 54             running1 = linebuffer;
 55             while ((token1 = strsep(&running1, "|")) != NULL) {
 56                 if (!command) {
 57                     /* The first token is the command */
 58                     command = token1;
 59                 }
 60                 else {
 61                 /* Token1 has something like "foo=bar" */
 62                     running2 = token1;
 63                     key = value = NULL;
 64                     while ((token2 = strsep(&running2, "=")) != NULL) {
 65                         if (!key) {
 66                             key = token2;
 67                         }
 68                         else if (!value) {
 69                             value = token2;
 70                         }
 71                     }
 72                 }
 73
 74                 if (strcmp(command, "CLIENT") == 0) {
 75                     /* This line has info about a client in the client list */
 76                     if (!client) {
 77                         /* Create a new client struct */
 78                         client = safe_malloc(sizeof(t_client));
 79                         memset(client, 0, sizeof(t_client));
 80                     }
 81                 }
 82
 83                 if (key && value) {
 84                     if (strcmp(command, "CLIENT") == 0) {
 85                         /* Assign the key into the appropriate slot in the connection structure */
 86                         if (strcmp(key, "ip") == 0) {
 87                             client->ip = safe_strdup(value);
 88                         }
 89                         else if (strcmp(key, "mac") == 0) {
 90                             client->mac = safe_strdup(value);
 91                         }
 92                         else if (strcmp(key, "token") == 0) {
 93                             client->token = safe_strdup(value);
 94                         }
 95                         else if (strcmp(key, "fw_connection_state") == 0) {
 96                             client->fw_connection_state = atoi(value);
 97                         }
 98                         else if (strcmp(key, "fd") == 0) {
 99                             client->fd = atoi(value);
100                         }
101                         else if (strcmp(key, "counters_incoming") == 0) {
102                             client->counters.incoming_history = atoll(value);
103                             client->counters.incoming = client->counters.incoming_history;
104                         }
105                         else if (strcmp(key, "counters_outgoing") == 0) {
106                             client->counters.outgoing_history = atoll(value);
107                             client->counters.outgoing = client->counters.outgoing_history;
108                         }
109                         else if (strcmp(key, "counters_last_updated") == 0) {
110                             client->counters.last_updated = atol(value);
111                         }
112                         else {
113                             debug(LOG_NOTICE, "I don‘t know how to inherit key [%s] value [%s] from parent", key, value);
114                         }
115                     }
116                 }
117             }
118
119             /* End of parsing this command */
120             if (client) {
121                 /* Add this client to the client list */
122                 if (!firstclient) {
123                     firstclient = client;
124                     lastclient = firstclient;
125                 }
126                 else {
127                     lastclient->next = client;
128                     lastclient = client;
129                 }
130             }
131
132             /* Clean up */
133             command = NULL;
134             memset(linebuffer, 0, sizeof(linebuffer));
135             len = 0;
136             client = NULL;
137         }
138     }
139
140     UNLOCK_CLIENT_LIST();
141     debug(LOG_INFO, "Client list downloaded successfully from parent");
142
143     close(sock);
144 }

代码片段1.3(已启动的wifidog发送客户端列表到新启动的wifidog):

  1 //thread_wdctl_handler(void *arg)函数是wifidog创建的控制线程,主要用于与wdctrl进行socket通信,根据wdctrl命令执行不同的操作。这里我们着重讲解的是wdctrl发送restart后wifidog的执行逻辑。
  2 static void *
  3 thread_wdctl_handler(void *arg)
  4 {
  5     int    fd,
  6         done,
  7         i;
  8     char    request[MAX_BUF];
  9     ssize_t    read_bytes,
 10         len;
 11
 12     debug(LOG_DEBUG, "Entering thread_wdctl_handler....");
 13
 14     fd = (int)arg;
 15
 16     debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);
 17
 18     /* 初始化变量 */
 19     read_bytes = 0;
 20     done = 0;
 21     memset(request, 0, sizeof(request));
 22
 23     /* 读取命令 */
 24     while (!done && read_bytes < (sizeof(request) - 1)) {
 25         len = read(fd, request + read_bytes,
 26                 sizeof(request) - read_bytes);    //读取wdctrl发送的命令
 27
 28         /* 判断命令正确性 */
 29         for (i = read_bytes; i < (read_bytes + len); i++) {
 30             if (request[i] == ‘\r‘ || request[i] == ‘\n‘) {
 31                 request[i] = ‘\0‘;
 32                 done = 1;
 33             }
 34         }
 35
 36         /* Increment position */
 37         read_bytes += len;
 38     }
 39
 40         //判断命令
 41     if (strncmp(request, "status", 6) == 0) {
 42         wdctl_status(fd);
 43     } else if (strncmp(request, "stop", 4) == 0) {
 44         wdctl_stop(fd);
 45     } else if (strncmp(request, "reset", 5) == 0) {
 46         wdctl_reset(fd, (request + 6));
 47     } else if (strncmp(request, "restart", 7) == 0) {
 48         wdctl_restart(fd);    //执行wdctl_restart(int afd)函数
 49     }
 50
 51     if (!done) {
 52         debug(LOG_ERR, "Invalid wdctl request.");
 53                 //关闭套接字
 54         shutdown(fd, 2);
 55         close(fd);
 56         pthread_exit(NULL);
 57     }
 58
 59     debug(LOG_DEBUG, "Request received: [%s]", request);
 60
 61         //关闭套接字
 62     shutdown(fd, 2);
 63     close(fd);
 64     debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");
 65
 66     return NULL;
 67 }
 68
 69
 70 //wdctl_restart(int afd)函数详解
 71 static void
 72 wdctl_restart(int afd)
 73 {
 74     int    sock,
 75         fd;
 76     char    *sock_name;
 77     struct     sockaddr_un    sa_un;
 78     s_config * conf = NULL;
 79     t_client * client = NULL;
 80     char * tempstring = NULL;
 81     pid_t pid;
 82     ssize_t written;
 83     socklen_t len;
 84
 85     conf = config_get_config();
 86
 87     debug(LOG_NOTICE, "Will restart myself");
 88
 89     /*
 90      * 准备内部连接socket
 91      */
 92     memset(&sa_un, 0, sizeof(sa_un));
 93     sock_name = conf->internal_sock;    //conf->internal_sock值为"/tmp/wifidog.sock"
 94     debug(LOG_DEBUG, "Socket name: %s", sock_name);
 95
 96     if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
 97
 98         debug(LOG_ERR, "INTERNAL socket name too long");
 99         return;
100     }
101
102     debug(LOG_DEBUG, "Creating socket");
103     sock = socket(PF_UNIX, SOCK_STREAM, 0);    //建立内部socket套接字
104
105     debug(LOG_DEBUG, "Got internal socket %d", sock);
106
107     /* 如果sock_name文件存在,则删除*/
108     unlink(sock_name);
109
110     debug(LOG_DEBUG, "Filling sockaddr_un");
111     strcpy(sa_un.sun_path, sock_name);
112     sa_un.sun_family = AF_UNIX;
113
114     debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
115
116
117     if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
118         debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
119         return;
120     }
121
122     if (listen(sock, 5)) {
123         debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
124         return;
125     }
126
127     /*
128      * socket建立完成,创建子进程
129      */
130     debug(LOG_DEBUG, "Forking in preparation for exec()...");
131     pid = safe_fork();
132     if (pid > 0) {
133         /* 父进程 */
134
135         /* 等待子进程连接此socket :*/
136         debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
137         len = sizeof(sa_un);
138         if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){    //接受连接
139             debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
140             close(sock);
141             return;
142         }
143
144         close(sock);
145
146         debug(LOG_DEBUG, "Received connection from child.  Sending them all existing clients");
147
148         /*子进程已经完成连接,发送客户端列表 */
149         LOCK_CLIENT_LIST();
150         client = client_get_first_client();    //获取第一个客户端
151         while (client) {
152             /* Send this client */
153             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);
154             debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
155             len = 0;
156             while (len != strlen(tempstring)) {
157                 written = write(fd, (tempstring + len), strlen(tempstring) - len);    //发送给子进程
158                 if (written == -1) {
159                     debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
160                     free(tempstring);
161                     break;
162                 }
163                 else {
164                     len += written;
165                 }
166             }
167             free(tempstring);
168             client = client->next;
169         }
170         UNLOCK_CLIENT_LIST();
171
172         close(fd);
173
174         debug(LOG_INFO, "Sent all existing clients to child.  Committing suicide!");
175
176         shutdown(afd, 2);
177         close(afd);
178
179
180         wdctl_stop(afd);
181     }
182     else {
183         /* 子进程,先关闭资源 */
184         close(wdctl_socket_server);
185         close(icmp_fd);
186         close(sock);
187         shutdown(afd, 2);
188         close(afd);
189         debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);
190
191         setsid();
192         execvp(restartargv[0], restartargv);    //执行外部命令,这里重新启动wifidog
193
194         debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
195         debug(LOG_ERR, "Exiting without cleanup");
196         exit(1);
197     }
198 }
时间: 2024-10-01 02:56:44

wifidog源码分析 - 初始化阶段的相关文章

S5PV210-uboot源码分析-第一阶段

uboot源码分析1-启动第一阶段 1.starts.S是我们uboot源码的第一阶段: 从u-boot.lds链接脚本中也可以看出start.S是我们整个程序的入口处,怎么看出的呢,因为在链接脚本中有个ENTRY(_start)声明了_start是程序的入口.所以_start符号所在的文件,就是我们整个程序的起始文件,_start所在处的代码就是我们整个程序的起始代码. 2.我们知道了程序的入口是_start这个符号,但是却不知道是在哪一个文件中,所以要SI进行查找搜索,点击SI的大R进行搜索

linux调度器源码分析 - 初始化(二)

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明调度器在系统启动初始化阶段是如何初始化和工作的.通过上期文章我们知道,在多核CPU和SMP系统中,每个CPU(多核COU中的每个核)都有自己的struct rq队列,而rq队列中又有着自己的struct cfs_rq和struct rt_rq.在初始化时就是对这三个结构进行初始化. init_tas

linux中断源码分析 - 初始化(二)

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的数据结构,分别是:中断描述符表,中断描述符数组,中断描述符,中断控制器描述符,中断服务例程.可以说这几个结构组成了整个内核中断框架主体,所以内核对整个中断的初始化工作大多集中在了这几个结构上. 在系统中,当一个中断产生时,首先CPU会从中断描述符表中获取相应的中断向量,并根据中断向量的权限位判断是否

wifidog源码分析 - wifidog原理

wifidog是一个用于配合认证服务器实现无线网页认证功能的程序,常见的情景就是使用于公共场合的无线wifi接入点,首先移动设备会连接公共wifi接入点,之后会弹出网页要求输入用户名密码,认证过后才能够连入外网.其主页是http://dev.wifidog.org/ 实现原理 其实wifidog原理很简单,主要是通过管控iptables,配合认证服务器进行客户端的放行操作.wifidog在启动后都会自动启动三个线程,分别为客户端检测线程.wdctrl交互线程.认证服务器心跳检测线程.每当新用户连

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(1)---fdevents结构体和fdevent系统对外接口

前面讲了lighttpd的插件系统,这一篇将看一看lighttpd中的fdevent系统.fdevent系统主要是处理各种IO事件,在web服务器中,主要就是向socket写数据和从socket读数据.通常,web服务器是IO密集型程序,这就要求在数据的读写上,web服务器必须能够具有很好的性能,不会因为某个socket的阻塞而致使其他socket也被阻塞,否则会大大降低服务器的性能.因此,大部分的web服务器都采用非阻塞IO进行数据的读写.lighttpd通过fdevent系统,采用类似OO中

wifidog源码分析Lighttpd1.4.20源码分析之插件系统(3)---PLUGIN_TO_SLOT宏

前面讲了lighttpd插件系统的加载和初始化,这一篇中,将介绍一下plugin.c中的宏PLUGIN_TO_SLOT.在将PLUGIN_TO_SLOT宏之前,我们先来看看lighttpd中插件系统的对外接口.这个接口所对的“外”指的是lighttpd服务器.前面已经提到,在运行的过程中,lighttpd不知道所加载的插件都是干什么用的,只知道这些插件所实现的接口,也就是在plugin结构体中那些函数指针有哪些对于某个插件是NULL,哪些是具体的函数地址.既然lighttpd只知道这些,那么它又

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

引言 当wifidog启动时,会启动一个线程(thread_client_timeout_check)维护客户端列表,具体就是wifidog必须定时检测客户端列表中的每个客户端是否在线,而wifidog是通过两种方式进行检测客户端在线情况,一种是定时通过iptables获取客户端出入总流量更新客户端时间,通过最近更新时间进行判断(有新的出入流量则更新客户端时间,之后使用最新客户端时间与当前时间判断),一种是查询认证服务器,通过认证服务器的返回信息进行判断(将客户端IP和状态请求发送给认证服务器,

wifidog源码分析Lighttpd1.4.20源码分析之fdevent系统(3) -----使用

接着上文介绍的函数fdevent_linux_sysepoll_event_add 讲解,首先看函数的第三个参数events,他是一个整型,其没以为对应一种IO事件.上面fdevent_event_add()函数的额第三个参数是FDEVENT_IN,这是一个宏 /* * 用于标记文件描述符的状态 */ #define FDEVENT_IN BV(0) //文件描述符是否可写 #define FDEVENT_PRI BV(1) //不阻塞的可读高优先级的数据 poll #define FDEVEN

flannel源码分析---初始化部分

// main.go 1.func main() ..... 首先调用sm, err := newSubnetManager()创建subnet manager .... 调用ctx, cancel := context.WithCancel(context.Background()) 调用nm, err := network.NewNetworkManager(ctx, sm) 创建runFunc = func(ctx context.Context) { nm.Run(ctx) } 创建一个