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
 */
 44     while (read(sock, &onechar, 1) == 1) {
 45         if (onechar == '
') {
 46             /* 如果接收到末尾('
'),则转为'' */
 47             onechar = '';
 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] == '
' || request[i] == '
') {
 31                 request[i] = '';
 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
", 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 }

小结

  客户端列表只有在restart命令中才会执行,实际上流程就是

  • 父wifidog准备socket
  • 父wifidog启动子wifidog
  • 子wifidog连接父wifidog
  • 客户端列表传递
  • 子wifidog终止父wifidog
原文地址:https://www.cnblogs.com/tolimit/p/4221719.html